2836 lines
103 KiB
Diff
2836 lines
103 KiB
Diff
From f5e635addaef59159bf6bc529b17954eda3684a1 Mon Sep 17 00:00:00 2001
|
|
From: FearlessTobi <thm.frey@gmail.com>
|
|
Date: Sun, 31 Jul 2022 04:46:26 +0200
|
|
Subject: [PATCH 1/5] ldn: Initial implementation
|
|
|
|
---
|
|
src/core/CMakeLists.txt | 2 +
|
|
src/core/hle/service/ldn/lan_discovery.cpp | 644 +++++++++++++++++++++
|
|
src/core/hle/service/ldn/lan_discovery.h | 133 +++++
|
|
src/core/hle/service/ldn/ldn.cpp | 229 ++++----
|
|
src/core/hle/service/ldn/ldn_types.h | 50 +-
|
|
src/core/internal_network/socket_proxy.cpp | 8 +-
|
|
src/dedicated_room/yuzu_room.cpp | 3 +-
|
|
src/network/room.cpp | 63 ++
|
|
src/network/room.h | 1 +
|
|
src/network/room_member.cpp | 57 ++
|
|
src/network/room_member.h | 35 +-
|
|
src/yuzu/main.cpp | 4 +-
|
|
src/yuzu/main.ui | 14 +
|
|
src/yuzu/multiplayer/chat_room.cpp | 12 +-
|
|
src/yuzu/multiplayer/state.cpp | 1 +
|
|
15 files changed, 1132 insertions(+), 124 deletions(-)
|
|
create mode 100644 src/core/hle/service/ldn/lan_discovery.cpp
|
|
create mode 100644 src/core/hle/service/ldn/lan_discovery.h
|
|
|
|
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
|
|
index 806e7ff6c0c1..52017878c9da 100644
|
|
--- a/src/core/CMakeLists.txt
|
|
+++ b/src/core/CMakeLists.txt
|
|
@@ -500,6 +500,8 @@ add_library(core STATIC
|
|
hle/service/jit/jit.h
|
|
hle/service/lbl/lbl.cpp
|
|
hle/service/lbl/lbl.h
|
|
+ hle/service/ldn/lan_discovery.cpp
|
|
+ hle/service/ldn/lan_discovery.h
|
|
hle/service/ldn/ldn_results.h
|
|
hle/service/ldn/ldn.cpp
|
|
hle/service/ldn/ldn.h
|
|
diff --git a/src/core/hle/service/ldn/lan_discovery.cpp b/src/core/hle/service/ldn/lan_discovery.cpp
|
|
new file mode 100644
|
|
index 000000000000..b04c990771ad
|
|
--- /dev/null
|
|
+++ b/src/core/hle/service/ldn/lan_discovery.cpp
|
|
@@ -0,0 +1,644 @@
|
|
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
|
+// SPDX-License-Identifier: GPL-2.0-or-later
|
|
+
|
|
+#include "core/hle/service/ldn/lan_discovery.h"
|
|
+#include "core/internal_network/network.h"
|
|
+#include "core/internal_network/network_interface.h"
|
|
+
|
|
+namespace Service::LDN {
|
|
+
|
|
+LanStation::LanStation(s8 node_id_, LANDiscovery* discovery_)
|
|
+ : node_info(nullptr), status(NodeStatus::Disconnected), node_id(node_id_),
|
|
+ discovery(discovery_) {}
|
|
+
|
|
+LanStation::~LanStation() = default;
|
|
+
|
|
+NodeStatus LanStation::GetStatus() const {
|
|
+ return status;
|
|
+}
|
|
+
|
|
+void LanStation::OnClose() {
|
|
+ LOG_INFO(Service_LDN, "OnClose {}", node_id);
|
|
+ Reset();
|
|
+ discovery->UpdateNodes();
|
|
+}
|
|
+
|
|
+void LanStation::Reset() {
|
|
+ status = NodeStatus::Disconnected;
|
|
+};
|
|
+
|
|
+void LanStation::OverrideInfo() {
|
|
+ bool connected = GetStatus() == NodeStatus::Connected;
|
|
+ node_info->node_id = node_id;
|
|
+ node_info->is_connected = connected ? 1 : 0;
|
|
+}
|
|
+
|
|
+LANDiscovery::LANDiscovery(Network::RoomNetwork& room_network_)
|
|
+ : stations({{{1, this}, {2, this}, {3, this}, {4, this}, {5, this}, {6, this}, {7, this}}}),
|
|
+ room_network{room_network_} {
|
|
+ LOG_INFO(Service_LDN, "LANDiscovery");
|
|
+}
|
|
+
|
|
+LANDiscovery::~LANDiscovery() {
|
|
+ LOG_INFO(Service_LDN, "~LANDiscovery");
|
|
+ if (inited) {
|
|
+ Result rc = Finalize();
|
|
+ LOG_INFO(Service_LDN, "Finalize: {}", rc.raw);
|
|
+ }
|
|
+}
|
|
+
|
|
+void LANDiscovery::InitNetworkInfo() {
|
|
+ network_info.common.bssid = GetFakeMac();
|
|
+ network_info.common.channel = WifiChannel::Wifi24_6;
|
|
+ network_info.common.link_level = LinkLevel::Good;
|
|
+ network_info.common.network_type = PackedNetworkType::Ldn;
|
|
+ network_info.common.ssid = fake_ssid;
|
|
+
|
|
+ auto& nodes = network_info.ldn.nodes;
|
|
+ for (std::size_t i = 0; i < NodeCountMax; i++) {
|
|
+ nodes[i].node_id = static_cast<s8>(i);
|
|
+ nodes[i].is_connected = 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+void LANDiscovery::InitNodeStateChange() {
|
|
+ for (auto& node_update : nodeChanges) {
|
|
+ node_update.state_change = NodeStateChange::None;
|
|
+ }
|
|
+ for (auto& node_state : node_last_states) {
|
|
+ node_state = 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+State LANDiscovery::GetState() const {
|
|
+ return state;
|
|
+}
|
|
+
|
|
+void LANDiscovery::SetState(State new_state) {
|
|
+ state = new_state;
|
|
+}
|
|
+
|
|
+Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network) const {
|
|
+ if (state == State::AccessPointCreated || state == State::StationConnected) {
|
|
+ std::memcpy(&out_network, &network_info, sizeof(network_info));
|
|
+ return ResultSuccess;
|
|
+ }
|
|
+
|
|
+ return ResultBadState;
|
|
+}
|
|
+
|
|
+Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network,
|
|
+ std::vector<NodeLatestUpdate>& out_updates,
|
|
+ std::size_t buffer_count) {
|
|
+ if (buffer_count > NodeCountMax) {
|
|
+ return ResultInvalidBufferCount;
|
|
+ }
|
|
+
|
|
+ if (state == State::AccessPointCreated || state == State::StationConnected) {
|
|
+ std::memcpy(&out_network, &network_info, sizeof(network_info));
|
|
+ for (std::size_t i = 0; i < buffer_count; i++) {
|
|
+ out_updates[i].state_change = nodeChanges[i].state_change;
|
|
+ nodeChanges[i].state_change = NodeStateChange::None;
|
|
+ }
|
|
+ return ResultSuccess;
|
|
+ }
|
|
+
|
|
+ return ResultBadState;
|
|
+}
|
|
+
|
|
+DisconnectReason LANDiscovery::GetDisconnectReason() const {
|
|
+ return disconnect_reason;
|
|
+}
|
|
+
|
|
+Result LANDiscovery::Scan(std::vector<NetworkInfo>& networks, u16& count,
|
|
+ const ScanFilter& filter) {
|
|
+ if (!IsFlagSet(filter.flag, ScanFilterFlag::NetworkType) ||
|
|
+ filter.network_type <= NetworkType::All) {
|
|
+ if (!IsFlagSet(filter.flag, ScanFilterFlag::Ssid) && filter.ssid.length >= SsidLengthMax) {
|
|
+ return ResultBadInput;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ {
|
|
+ std::scoped_lock lock{packet_mutex};
|
|
+ scan_results.clear();
|
|
+
|
|
+ SendBroadcast(Network::LDNPacketType::Scan);
|
|
+ }
|
|
+
|
|
+ LOG_INFO(Service_LDN, "Waiting for scan replies");
|
|
+ std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
+
|
|
+ std::scoped_lock lock{packet_mutex};
|
|
+ for (const auto& [key, info] : scan_results) {
|
|
+ if (count >= networks.size()) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (IsFlagSet(filter.flag, ScanFilterFlag::LocalCommunicationId)) {
|
|
+ if (filter.network_id.intent_id.local_communication_id !=
|
|
+ info.network_id.intent_id.local_communication_id) {
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+ if (IsFlagSet(filter.flag, ScanFilterFlag::SessionId)) {
|
|
+ if (filter.network_id.session_id != info.network_id.session_id) {
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+ if (IsFlagSet(filter.flag, ScanFilterFlag::NetworkType)) {
|
|
+ if (filter.network_type != static_cast<NetworkType>(info.common.network_type)) {
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+ if (IsFlagSet(filter.flag, ScanFilterFlag::Ssid)) {
|
|
+ if (filter.ssid != info.common.ssid) {
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+ if (IsFlagSet(filter.flag, ScanFilterFlag::SceneId)) {
|
|
+ if (filter.network_id.intent_id.scene_id != info.network_id.intent_id.scene_id) {
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ networks[count++] = info;
|
|
+ }
|
|
+
|
|
+ return ResultSuccess;
|
|
+}
|
|
+
|
|
+Result LANDiscovery::SetAdvertiseData(std::vector<u8>& data) {
|
|
+ std::scoped_lock lock{packet_mutex};
|
|
+ std::size_t size = data.size();
|
|
+ if (size > AdvertiseDataSizeMax) {
|
|
+ return ResultAdvertiseDataTooLarge;
|
|
+ }
|
|
+
|
|
+ std::memcpy(network_info.ldn.advertise_data.data(), data.data(), size);
|
|
+ network_info.ldn.advertise_data_size = static_cast<u16>(size);
|
|
+
|
|
+ UpdateNodes();
|
|
+
|
|
+ return ResultSuccess;
|
|
+}
|
|
+
|
|
+Result LANDiscovery::OpenAccessPoint() {
|
|
+ std::scoped_lock lock{packet_mutex};
|
|
+ disconnect_reason = DisconnectReason::None;
|
|
+ if (state == State::None) {
|
|
+ return ResultBadState;
|
|
+ }
|
|
+
|
|
+ ResetStations();
|
|
+ SetState(State::AccessPointOpened);
|
|
+
|
|
+ return ResultSuccess;
|
|
+}
|
|
+
|
|
+Result LANDiscovery::CloseAccessPoint() {
|
|
+ std::scoped_lock lock{packet_mutex};
|
|
+ if (state == State::None) {
|
|
+ return ResultBadState;
|
|
+ }
|
|
+
|
|
+ if (state == State::AccessPointCreated) {
|
|
+ DestroyNetwork();
|
|
+ }
|
|
+
|
|
+ ResetStations();
|
|
+ SetState(State::Initialized);
|
|
+
|
|
+ return ResultSuccess;
|
|
+}
|
|
+
|
|
+Result LANDiscovery::OpenStation() {
|
|
+ std::scoped_lock lock{packet_mutex};
|
|
+ disconnect_reason = DisconnectReason::None;
|
|
+ if (state == State::None) {
|
|
+ return ResultBadState;
|
|
+ }
|
|
+
|
|
+ ResetStations();
|
|
+ SetState(State::StationOpened);
|
|
+
|
|
+ return ResultSuccess;
|
|
+}
|
|
+
|
|
+Result LANDiscovery::CloseStation() {
|
|
+ std::scoped_lock lock{packet_mutex};
|
|
+ if (state == State::None) {
|
|
+ return ResultBadState;
|
|
+ }
|
|
+
|
|
+ if (state == State::StationConnected) {
|
|
+ Disconnect();
|
|
+ }
|
|
+
|
|
+ ResetStations();
|
|
+ SetState(State::Initialized);
|
|
+
|
|
+ return ResultSuccess;
|
|
+}
|
|
+
|
|
+Result LANDiscovery::CreateNetwork(const SecurityConfig& security_config,
|
|
+ const UserConfig& user_config,
|
|
+ const NetworkConfig& network_config) {
|
|
+ std::scoped_lock lock{packet_mutex};
|
|
+
|
|
+ if (state != State::AccessPointOpened) {
|
|
+ return ResultBadState;
|
|
+ }
|
|
+
|
|
+ InitNetworkInfo();
|
|
+ network_info.ldn.node_count_max = network_config.node_count_max;
|
|
+ network_info.ldn.security_mode = security_config.security_mode;
|
|
+
|
|
+ if (network_config.channel == WifiChannel::Default) {
|
|
+ network_info.common.channel = WifiChannel::Wifi24_6;
|
|
+ } else {
|
|
+ network_info.common.channel = network_config.channel;
|
|
+ }
|
|
+
|
|
+ std::independent_bits_engine<std::mt19937, 64, u64> bits_engine;
|
|
+ network_info.network_id.session_id.high = bits_engine();
|
|
+ network_info.network_id.session_id.low = bits_engine();
|
|
+ network_info.network_id.intent_id = network_config.intent_id;
|
|
+
|
|
+ NodeInfo& node0 = network_info.ldn.nodes[0];
|
|
+ const Result rc2 = GetNodeInfo(node0, user_config, network_config.local_communication_version);
|
|
+ if (rc2.IsError()) {
|
|
+ return ResultAccessPointConnectionFailed;
|
|
+ }
|
|
+
|
|
+ SetState(State::AccessPointCreated);
|
|
+
|
|
+ InitNodeStateChange();
|
|
+ node0.is_connected = 1;
|
|
+ UpdateNodes();
|
|
+
|
|
+ return rc2;
|
|
+}
|
|
+
|
|
+Result LANDiscovery::DestroyNetwork() {
|
|
+ for (auto local_ip : connected_clients) {
|
|
+ SendPacket(Network::LDNPacketType::DestroyNetwork, local_ip);
|
|
+ }
|
|
+
|
|
+ ResetStations();
|
|
+
|
|
+ SetState(State::AccessPointOpened);
|
|
+ LanEvent();
|
|
+
|
|
+ return ResultSuccess;
|
|
+}
|
|
+
|
|
+Result LANDiscovery::Connect(const NetworkInfo& network_info_, const UserConfig& user_config,
|
|
+ u16 local_communication_version) {
|
|
+ std::scoped_lock lock{packet_mutex};
|
|
+ if (network_info_.ldn.node_count == 0) {
|
|
+ return ResultInvalidNodeCount;
|
|
+ }
|
|
+
|
|
+ Result rc = GetNodeInfo(node_info, user_config, local_communication_version);
|
|
+ if (rc.IsError()) {
|
|
+ return ResultConnectionFailed;
|
|
+ }
|
|
+
|
|
+ Ipv4Address node_host = network_info_.ldn.nodes[0].ipv4_address;
|
|
+ std::reverse(std::begin(node_host), std::end(node_host)); // htonl
|
|
+ host_ip = node_host;
|
|
+ SendPacket(Network::LDNPacketType::Connect, node_info, *host_ip);
|
|
+
|
|
+ InitNodeStateChange();
|
|
+
|
|
+ std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
+
|
|
+ return ResultSuccess;
|
|
+}
|
|
+
|
|
+Result LANDiscovery::Disconnect() {
|
|
+ if (host_ip) {
|
|
+ SendPacket(Network::LDNPacketType::Disconnect, node_info, *host_ip);
|
|
+ }
|
|
+
|
|
+ SetState(State::StationOpened);
|
|
+ LanEvent();
|
|
+
|
|
+ return ResultSuccess;
|
|
+}
|
|
+
|
|
+Result LANDiscovery::Initialize(LanEventFunc lan_event, bool listening) {
|
|
+ std::scoped_lock lock{packet_mutex};
|
|
+ if (inited) {
|
|
+ return ResultSuccess;
|
|
+ }
|
|
+
|
|
+ for (auto& station : stations) {
|
|
+ station.discovery = this;
|
|
+ station.node_info = &network_info.ldn.nodes[station.node_id];
|
|
+ station.Reset();
|
|
+ }
|
|
+
|
|
+ connected_clients.clear();
|
|
+ LanEvent = lan_event;
|
|
+
|
|
+ SetState(State::Initialized);
|
|
+
|
|
+ inited = true;
|
|
+ return ResultSuccess;
|
|
+}
|
|
+
|
|
+Result LANDiscovery::Finalize() {
|
|
+ std::scoped_lock lock{packet_mutex};
|
|
+ Result rc = ResultSuccess;
|
|
+
|
|
+ if (inited) {
|
|
+ if (state == State::AccessPointCreated) {
|
|
+ DestroyNetwork();
|
|
+ }
|
|
+ if (state == State::StationConnected) {
|
|
+ Disconnect();
|
|
+ }
|
|
+
|
|
+ ResetStations();
|
|
+ inited = false;
|
|
+ }
|
|
+
|
|
+ SetState(State::None);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+void LANDiscovery::ResetStations() {
|
|
+ for (auto& station : stations) {
|
|
+ station.Reset();
|
|
+ }
|
|
+ connected_clients.clear();
|
|
+}
|
|
+
|
|
+void LANDiscovery::UpdateNodes() {
|
|
+ u8 count = 0;
|
|
+ for (auto& station : stations) {
|
|
+ bool connected = station.GetStatus() == NodeStatus::Connected;
|
|
+ if (connected) {
|
|
+ count++;
|
|
+ }
|
|
+ station.OverrideInfo();
|
|
+ }
|
|
+ network_info.ldn.node_count = count + 1;
|
|
+
|
|
+ for (auto local_ip : connected_clients) {
|
|
+ SendPacket(Network::LDNPacketType::SyncNetwork, network_info, local_ip);
|
|
+ }
|
|
+
|
|
+ OnNetworkInfoChanged();
|
|
+}
|
|
+
|
|
+void LANDiscovery::OnSyncNetwork(const NetworkInfo& info) {
|
|
+ network_info = info;
|
|
+ if (state == State::StationOpened) {
|
|
+ SetState(State::StationConnected);
|
|
+ }
|
|
+ OnNetworkInfoChanged();
|
|
+}
|
|
+
|
|
+void LANDiscovery::OnDisconnectFromHost() {
|
|
+ LOG_INFO(Service_LDN, "OnDisconnectFromHost state: {}", static_cast<int>(state));
|
|
+ host_ip = std::nullopt;
|
|
+ if (state == State::StationConnected) {
|
|
+ SetState(State::StationOpened);
|
|
+ LanEvent();
|
|
+ }
|
|
+}
|
|
+
|
|
+void LANDiscovery::OnNetworkInfoChanged() {
|
|
+ if (IsNodeStateChanged()) {
|
|
+ LanEvent();
|
|
+ }
|
|
+ return;
|
|
+}
|
|
+
|
|
+Network::IPv4Address LANDiscovery::GetLocalIp() const {
|
|
+ Network::IPv4Address local_ip{0xFF, 0xFF, 0xFF, 0xFF};
|
|
+ if (auto room_member = room_network.GetRoomMember().lock()) {
|
|
+ if (room_member->IsConnected()) {
|
|
+ local_ip = room_member->GetFakeIpAddress();
|
|
+ }
|
|
+ }
|
|
+ return local_ip;
|
|
+}
|
|
+
|
|
+template <typename Data>
|
|
+void LANDiscovery::SendPacket(Network::LDNPacketType type, const Data& data,
|
|
+ Ipv4Address remote_ip) {
|
|
+ Network::LDNPacket packet;
|
|
+ packet.type = type;
|
|
+
|
|
+ packet.broadcast = false;
|
|
+ packet.local_ip = GetLocalIp();
|
|
+ packet.remote_ip = remote_ip;
|
|
+
|
|
+ packet.data.clear();
|
|
+ packet.data.resize(sizeof(data));
|
|
+ std::memcpy(packet.data.data(), &data, sizeof(data));
|
|
+ SendPacket(packet);
|
|
+}
|
|
+
|
|
+void LANDiscovery::SendPacket(Network::LDNPacketType type, Ipv4Address remote_ip) {
|
|
+ Network::LDNPacket packet;
|
|
+ packet.type = type;
|
|
+
|
|
+ packet.broadcast = false;
|
|
+ packet.local_ip = GetLocalIp();
|
|
+ packet.remote_ip = remote_ip;
|
|
+
|
|
+ packet.data.clear();
|
|
+ SendPacket(packet);
|
|
+}
|
|
+
|
|
+template <typename Data>
|
|
+void LANDiscovery::SendBroadcast(Network::LDNPacketType type, const Data& data) {
|
|
+ Network::LDNPacket packet;
|
|
+ packet.type = type;
|
|
+
|
|
+ packet.broadcast = true;
|
|
+ packet.local_ip = GetLocalIp();
|
|
+
|
|
+ packet.data.clear();
|
|
+ packet.data.resize(sizeof(data));
|
|
+ std::memcpy(packet.data.data(), &data, sizeof(data));
|
|
+ SendPacket(packet);
|
|
+}
|
|
+
|
|
+void LANDiscovery::SendBroadcast(Network::LDNPacketType type) {
|
|
+ Network::LDNPacket packet;
|
|
+ packet.type = type;
|
|
+
|
|
+ packet.broadcast = true;
|
|
+ packet.local_ip = GetLocalIp();
|
|
+
|
|
+ packet.data.clear();
|
|
+ SendPacket(packet);
|
|
+}
|
|
+
|
|
+void LANDiscovery::SendPacket(const Network::LDNPacket& packet) {
|
|
+ if (auto room_member = room_network.GetRoomMember().lock()) {
|
|
+ if (room_member->IsConnected()) {
|
|
+ room_member->SendLdnPacket(packet);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void LANDiscovery::ReceivePacket(const Network::LDNPacket& packet) {
|
|
+ std::scoped_lock lock{packet_mutex};
|
|
+ switch (packet.type) {
|
|
+ case Network::LDNPacketType::Scan: {
|
|
+ LOG_INFO(Frontend, "Scan packet received!");
|
|
+ if (state == State::AccessPointCreated) {
|
|
+ // Reply to the sender
|
|
+ SendPacket(Network::LDNPacketType::ScanResp, network_info, packet.local_ip);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case Network::LDNPacketType::ScanResp: {
|
|
+ LOG_INFO(Frontend, "ScanResp packet received!");
|
|
+
|
|
+ NetworkInfo info{};
|
|
+ std::memcpy(&info, packet.data.data(), sizeof(NetworkInfo));
|
|
+ scan_results.insert({info.common.bssid, info});
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ case Network::LDNPacketType::Connect: {
|
|
+ LOG_INFO(Frontend, "Connect packet received!");
|
|
+
|
|
+ NodeInfo info{};
|
|
+ std::memcpy(&info, packet.data.data(), sizeof(NodeInfo));
|
|
+
|
|
+ connected_clients.push_back(packet.local_ip);
|
|
+
|
|
+ for (LanStation& station : stations) {
|
|
+ if (station.status != NodeStatus::Connected) {
|
|
+ *station.node_info = info;
|
|
+ station.status = NodeStatus::Connected;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ UpdateNodes();
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ case Network::LDNPacketType::Disconnect: {
|
|
+ LOG_INFO(Frontend, "Disconnect packet received!");
|
|
+
|
|
+ connected_clients.erase(
|
|
+ std::remove(connected_clients.begin(), connected_clients.end(), packet.local_ip),
|
|
+ connected_clients.end());
|
|
+
|
|
+ NodeInfo info{};
|
|
+ std::memcpy(&info, packet.data.data(), sizeof(NodeInfo));
|
|
+
|
|
+ for (LanStation& station : stations) {
|
|
+ if (station.status == NodeStatus::Connected &&
|
|
+ station.node_info->mac_address == info.mac_address) {
|
|
+ station.OnClose();
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ case Network::LDNPacketType::DestroyNetwork: {
|
|
+ ResetStations();
|
|
+ OnDisconnectFromHost();
|
|
+ break;
|
|
+ }
|
|
+ case Network::LDNPacketType::SyncNetwork: {
|
|
+ if (state == State::StationOpened || state == State::StationConnected) {
|
|
+ LOG_INFO(Frontend, "SyncNetwork packet received!");
|
|
+ NetworkInfo info{};
|
|
+ std::memcpy(&info, packet.data.data(), sizeof(NetworkInfo));
|
|
+
|
|
+ OnSyncNetwork(info);
|
|
+ } else {
|
|
+ LOG_INFO(Frontend, "SyncNetwork packet received but in wrong State!");
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ default: {
|
|
+ LOG_INFO(Frontend, "ReceivePacket unhandled type {}", static_cast<int>(packet.type));
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+bool LANDiscovery::IsNodeStateChanged() {
|
|
+ bool changed = false;
|
|
+ const auto& nodes = network_info.ldn.nodes;
|
|
+ for (int i = 0; i < NodeCountMax; i++) {
|
|
+ if (nodes[i].is_connected != node_last_states[i]) {
|
|
+ if (nodes[i].is_connected) {
|
|
+ nodeChanges[i].state_change |= NodeStateChange::Connect;
|
|
+ } else {
|
|
+ nodeChanges[i].state_change |= NodeStateChange::Disconnect;
|
|
+ }
|
|
+ node_last_states[i] = nodes[i].is_connected;
|
|
+ changed = true;
|
|
+ }
|
|
+ }
|
|
+ return changed;
|
|
+}
|
|
+
|
|
+bool LANDiscovery::IsFlagSet(ScanFilterFlag flag, ScanFilterFlag search_flag) const {
|
|
+ const auto flag_value = static_cast<u32>(flag);
|
|
+ const auto search_flag_value = static_cast<u32>(search_flag);
|
|
+ return (flag_value & search_flag_value) == search_flag_value;
|
|
+}
|
|
+
|
|
+int LANDiscovery::GetStationCount() {
|
|
+ int count = 0;
|
|
+ for (const auto& station : stations) {
|
|
+ if (station.GetStatus() != NodeStatus::Disconnected) {
|
|
+ count++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+MacAddress LANDiscovery::GetFakeMac() const {
|
|
+ MacAddress mac{};
|
|
+ mac.raw[0] = 0x02;
|
|
+ mac.raw[1] = 0x00;
|
|
+
|
|
+ const auto ip = GetLocalIp();
|
|
+ memcpy(mac.raw.data() + 2, &ip, sizeof(ip));
|
|
+
|
|
+ return mac;
|
|
+}
|
|
+
|
|
+Result LANDiscovery::GetNodeInfo(NodeInfo& node, const UserConfig& userConfig,
|
|
+ u16 localCommunicationVersion) {
|
|
+ const auto network_interface = Network::GetSelectedNetworkInterface();
|
|
+
|
|
+ if (!network_interface) {
|
|
+ LOG_ERROR(Service_LDN, "No network interface available");
|
|
+ return ResultNoIpAddress;
|
|
+ }
|
|
+
|
|
+ node.mac_address = GetFakeMac();
|
|
+ node.is_connected = 1;
|
|
+ std::memcpy(node.user_name.data(), userConfig.user_name.data(), UserNameBytesMax + 1);
|
|
+ node.local_communication_version = localCommunicationVersion;
|
|
+
|
|
+ Ipv4Address current_address = GetLocalIp();
|
|
+ std::reverse(std::begin(current_address), std::end(current_address)); // ntohl
|
|
+ node.ipv4_address = current_address;
|
|
+
|
|
+ return ResultSuccess;
|
|
+}
|
|
+
|
|
+} // namespace Service::LDN
|
|
diff --git a/src/core/hle/service/ldn/lan_discovery.h b/src/core/hle/service/ldn/lan_discovery.h
|
|
new file mode 100644
|
|
index 000000000000..255342456eba
|
|
--- /dev/null
|
|
+++ b/src/core/hle/service/ldn/lan_discovery.h
|
|
@@ -0,0 +1,133 @@
|
|
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
|
+// SPDX-License-Identifier: GPL-2.0-or-later
|
|
+
|
|
+#pragma once
|
|
+
|
|
+#include <array>
|
|
+#include <cstring>
|
|
+#include <functional>
|
|
+#include <memory>
|
|
+#include <mutex>
|
|
+#include <optional>
|
|
+#include <random>
|
|
+#include <thread>
|
|
+#include <unordered_map>
|
|
+
|
|
+#include "common/logging/log.h"
|
|
+#include "common/socket_types.h"
|
|
+#include "core/hle/result.h"
|
|
+#include "core/hle/service/ldn/ldn_results.h"
|
|
+#include "core/hle/service/ldn/ldn_types.h"
|
|
+#include "network/network.h"
|
|
+
|
|
+namespace Service::LDN {
|
|
+
|
|
+class LANDiscovery;
|
|
+
|
|
+class LanStation {
|
|
+public:
|
|
+ LanStation(s8 node_id_, LANDiscovery* discovery_);
|
|
+ ~LanStation();
|
|
+
|
|
+ void OnClose();
|
|
+ NodeStatus GetStatus() const;
|
|
+ void Reset();
|
|
+ void OverrideInfo();
|
|
+
|
|
+protected:
|
|
+ friend class LANDiscovery;
|
|
+ NodeInfo* node_info;
|
|
+ NodeStatus status;
|
|
+ s8 node_id;
|
|
+ LANDiscovery* discovery;
|
|
+};
|
|
+
|
|
+class LANDiscovery {
|
|
+public:
|
|
+ typedef std::function<void()> LanEventFunc;
|
|
+
|
|
+ LANDiscovery(Network::RoomNetwork& room_network_);
|
|
+ ~LANDiscovery();
|
|
+
|
|
+ State GetState() const;
|
|
+ void SetState(State new_state);
|
|
+
|
|
+ Result GetNetworkInfo(NetworkInfo& out_network) const;
|
|
+ Result GetNetworkInfo(NetworkInfo& out_network, std::vector<NodeLatestUpdate>& out_updates,
|
|
+ std::size_t buffer_count);
|
|
+
|
|
+ DisconnectReason GetDisconnectReason() const;
|
|
+ Result Scan(std::vector<NetworkInfo>& networks, u16& count, const ScanFilter& filter);
|
|
+ Result SetAdvertiseData(std::vector<u8>& data);
|
|
+
|
|
+ Result OpenAccessPoint();
|
|
+ Result CloseAccessPoint();
|
|
+
|
|
+ Result OpenStation();
|
|
+ Result CloseStation();
|
|
+
|
|
+ Result CreateNetwork(const SecurityConfig& security_config, const UserConfig& user_config,
|
|
+ const NetworkConfig& network_config);
|
|
+ Result DestroyNetwork();
|
|
+
|
|
+ Result Connect(const NetworkInfo& network_info_, const UserConfig& user_config,
|
|
+ u16 local_communication_version);
|
|
+ Result Disconnect();
|
|
+
|
|
+ Result Initialize(LanEventFunc lan_event = empty_func, bool listening = true);
|
|
+ Result Finalize();
|
|
+
|
|
+ void ReceivePacket(const Network::LDNPacket& packet);
|
|
+
|
|
+protected:
|
|
+ friend class LanStation;
|
|
+
|
|
+ void InitNetworkInfo();
|
|
+ void InitNodeStateChange();
|
|
+
|
|
+ void ResetStations();
|
|
+ void UpdateNodes();
|
|
+
|
|
+ void OnSyncNetwork(const NetworkInfo& info);
|
|
+ void OnDisconnectFromHost();
|
|
+ void OnNetworkInfoChanged();
|
|
+
|
|
+ bool IsNodeStateChanged();
|
|
+ bool IsFlagSet(ScanFilterFlag flag, ScanFilterFlag search_flag) const;
|
|
+ int GetStationCount();
|
|
+ MacAddress GetFakeMac() const;
|
|
+ Result GetNodeInfo(NodeInfo& node, const UserConfig& user_config,
|
|
+ u16 local_communication_version);
|
|
+
|
|
+ Network::IPv4Address GetLocalIp() const;
|
|
+ template <typename Data>
|
|
+ void SendPacket(Network::LDNPacketType type, const Data& data, Ipv4Address remote_ip);
|
|
+ void SendPacket(Network::LDNPacketType type, Ipv4Address remote_ip);
|
|
+ template <typename Data>
|
|
+ void SendBroadcast(Network::LDNPacketType type, const Data& data);
|
|
+ void SendBroadcast(Network::LDNPacketType type);
|
|
+ void SendPacket(const Network::LDNPacket& packet);
|
|
+
|
|
+ static const LanEventFunc empty_func;
|
|
+ const Ssid fake_ssid{"YuzuFakeSsidForLdn"};
|
|
+
|
|
+ bool inited{};
|
|
+ std::mutex packet_mutex;
|
|
+ std::array<LanStation, StationCountMax> stations;
|
|
+ std::array<NodeLatestUpdate, NodeCountMax> nodeChanges{};
|
|
+ std::array<u8, NodeCountMax> node_last_states{};
|
|
+ std::unordered_map<MacAddress, NetworkInfo, MACAddressHash> scan_results{};
|
|
+ NodeInfo node_info{};
|
|
+ NetworkInfo network_info{};
|
|
+ State state{State::None};
|
|
+ DisconnectReason disconnect_reason{DisconnectReason::None};
|
|
+
|
|
+ // TODO (flTobi): Should this be an std::set?
|
|
+ std::vector<Ipv4Address> connected_clients;
|
|
+ std::optional<Ipv4Address> host_ip = std::nullopt;
|
|
+
|
|
+ LanEventFunc LanEvent;
|
|
+
|
|
+ Network::RoomNetwork& room_network;
|
|
+};
|
|
+} // namespace Service::LDN
|
|
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp
|
|
index c11daff547b6..6537f49cf2e9 100644
|
|
--- a/src/core/hle/service/ldn/ldn.cpp
|
|
+++ b/src/core/hle/service/ldn/ldn.cpp
|
|
@@ -4,11 +4,13 @@
|
|
#include <memory>
|
|
|
|
#include "core/core.h"
|
|
+#include "core/hle/service/ldn/lan_discovery.h"
|
|
#include "core/hle/service/ldn/ldn.h"
|
|
#include "core/hle/service/ldn/ldn_results.h"
|
|
#include "core/hle/service/ldn/ldn_types.h"
|
|
#include "core/internal_network/network.h"
|
|
#include "core/internal_network/network_interface.h"
|
|
+#include "network/network.h"
|
|
|
|
// This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent
|
|
#undef CreateEvent
|
|
@@ -105,13 +107,13 @@ class IUserLocalCommunicationService final
|
|
public:
|
|
explicit IUserLocalCommunicationService(Core::System& system_)
|
|
: ServiceFramework{system_, "IUserLocalCommunicationService", ServiceThreadType::CreateNew},
|
|
- service_context{system, "IUserLocalCommunicationService"}, room_network{
|
|
- system_.GetRoomNetwork()} {
|
|
+ service_context{system, "IUserLocalCommunicationService"},
|
|
+ room_network{system_.GetRoomNetwork()}, lan_discovery{room_network} {
|
|
// clang-format off
|
|
static const FunctionInfo functions[] = {
|
|
{0, &IUserLocalCommunicationService::GetState, "GetState"},
|
|
{1, &IUserLocalCommunicationService::GetNetworkInfo, "GetNetworkInfo"},
|
|
- {2, nullptr, "GetIpv4Address"},
|
|
+ {2, &IUserLocalCommunicationService::GetIpv4Address, "GetIpv4Address"},
|
|
{3, &IUserLocalCommunicationService::GetDisconnectReason, "GetDisconnectReason"},
|
|
{4, &IUserLocalCommunicationService::GetSecurityParameter, "GetSecurityParameter"},
|
|
{5, &IUserLocalCommunicationService::GetNetworkConfig, "GetNetworkConfig"},
|
|
@@ -119,7 +121,7 @@ class IUserLocalCommunicationService final
|
|
{101, &IUserLocalCommunicationService::GetNetworkInfoLatestUpdate, "GetNetworkInfoLatestUpdate"},
|
|
{102, &IUserLocalCommunicationService::Scan, "Scan"},
|
|
{103, &IUserLocalCommunicationService::ScanPrivate, "ScanPrivate"},
|
|
- {104, nullptr, "SetWirelessControllerRestriction"},
|
|
+ {104, &IUserLocalCommunicationService::SetWirelessControllerRestriction, "SetWirelessControllerRestriction"},
|
|
{200, &IUserLocalCommunicationService::OpenAccessPoint, "OpenAccessPoint"},
|
|
{201, &IUserLocalCommunicationService::CloseAccessPoint, "CloseAccessPoint"},
|
|
{202, &IUserLocalCommunicationService::CreateNetwork, "CreateNetwork"},
|
|
@@ -148,16 +150,30 @@ class IUserLocalCommunicationService final
|
|
}
|
|
|
|
~IUserLocalCommunicationService() {
|
|
+ if (is_initialized) {
|
|
+ if (auto room_member = room_network.GetRoomMember().lock()) {
|
|
+ room_member->Unbind(ldn_packet_received);
|
|
+ }
|
|
+ }
|
|
+
|
|
service_context.CloseEvent(state_change_event);
|
|
}
|
|
|
|
+ /// Callback to parse and handle a received LDN packet.
|
|
+ void OnLDNPacketReceived(const Network::LDNPacket& packet) {
|
|
+ lan_discovery.ReceivePacket(packet);
|
|
+ }
|
|
+
|
|
void OnEventFired() {
|
|
state_change_event->GetWritableEvent().Signal();
|
|
}
|
|
|
|
void GetState(Kernel::HLERequestContext& ctx) {
|
|
State state = State::Error;
|
|
- LOG_WARNING(Service_LDN, "(STUBBED) called, state = {}", state);
|
|
+
|
|
+ if (is_initialized) {
|
|
+ state = lan_discovery.GetState();
|
|
+ }
|
|
|
|
IPC::ResponseBuilder rb{ctx, 3};
|
|
rb.Push(ResultSuccess);
|
|
@@ -175,7 +191,7 @@ class IUserLocalCommunicationService final
|
|
}
|
|
|
|
NetworkInfo network_info{};
|
|
- const auto rc = ResultSuccess;
|
|
+ const auto rc = lan_discovery.GetNetworkInfo(network_info);
|
|
if (rc.IsError()) {
|
|
LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
@@ -183,28 +199,52 @@ class IUserLocalCommunicationService final
|
|
return;
|
|
}
|
|
|
|
- LOG_WARNING(Service_LDN, "(STUBBED) called, ssid='{}', nodes={}",
|
|
- network_info.common.ssid.GetStringValue(), network_info.ldn.node_count);
|
|
-
|
|
ctx.WriteBuffer<NetworkInfo>(network_info);
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
- rb.Push(rc);
|
|
+ rb.Push(ResultSuccess);
|
|
}
|
|
|
|
- void GetDisconnectReason(Kernel::HLERequestContext& ctx) {
|
|
- const auto disconnect_reason = DisconnectReason::None;
|
|
+ void GetIpv4Address(Kernel::HLERequestContext& ctx) {
|
|
+ LOG_CRITICAL(Service_LDN, "called");
|
|
|
|
- LOG_WARNING(Service_LDN, "(STUBBED) called, disconnect_reason={}", disconnect_reason);
|
|
+ const auto network_interface = Network::GetSelectedNetworkInterface();
|
|
+
|
|
+ if (!network_interface) {
|
|
+ LOG_ERROR(Service_LDN, "No network interface available");
|
|
+ IPC::ResponseBuilder rb{ctx, 2};
|
|
+ rb.Push(ResultNoIpAddress);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ Ipv4Address current_address{Network::TranslateIPv4(network_interface->ip_address)};
|
|
+ Ipv4Address subnet_mask{Network::TranslateIPv4(network_interface->subnet_mask)};
|
|
+
|
|
+ // When we're connected to a room, spoof the hosts IP address
|
|
+ if (auto room_member = room_network.GetRoomMember().lock()) {
|
|
+ if (room_member->IsConnected()) {
|
|
+ current_address = room_member->GetFakeIpAddress();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ std::reverse(std::begin(current_address), std::end(current_address)); // ntohl
|
|
+ std::reverse(std::begin(subnet_mask), std::end(subnet_mask)); // ntohl
|
|
+
|
|
+ IPC::ResponseBuilder rb{ctx, 4};
|
|
+ rb.Push(ResultSuccess);
|
|
+ rb.PushRaw(current_address);
|
|
+ rb.PushRaw(subnet_mask);
|
|
+ }
|
|
|
|
+ void GetDisconnectReason(Kernel::HLERequestContext& ctx) {
|
|
IPC::ResponseBuilder rb{ctx, 3};
|
|
rb.Push(ResultSuccess);
|
|
- rb.PushEnum(disconnect_reason);
|
|
+ rb.PushEnum(lan_discovery.GetDisconnectReason());
|
|
}
|
|
|
|
void GetSecurityParameter(Kernel::HLERequestContext& ctx) {
|
|
SecurityParameter security_parameter{};
|
|
NetworkInfo info{};
|
|
- const Result rc = ResultSuccess;
|
|
+ const Result rc = lan_discovery.GetNetworkInfo(info);
|
|
|
|
if (rc.IsError()) {
|
|
LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
|
|
@@ -217,8 +257,6 @@ class IUserLocalCommunicationService final
|
|
std::memcpy(security_parameter.data.data(), info.ldn.security_parameter.data(),
|
|
sizeof(SecurityParameter::data));
|
|
|
|
- LOG_WARNING(Service_LDN, "(STUBBED) called");
|
|
-
|
|
IPC::ResponseBuilder rb{ctx, 10};
|
|
rb.Push(rc);
|
|
rb.PushRaw<SecurityParameter>(security_parameter);
|
|
@@ -227,7 +265,7 @@ class IUserLocalCommunicationService final
|
|
void GetNetworkConfig(Kernel::HLERequestContext& ctx) {
|
|
NetworkConfig config{};
|
|
NetworkInfo info{};
|
|
- const Result rc = ResultSuccess;
|
|
+ const Result rc = lan_discovery.GetNetworkInfo(info);
|
|
|
|
if (rc.IsError()) {
|
|
LOG_ERROR(Service_LDN, "NetworkConfig is not valid {}", rc.raw);
|
|
@@ -241,12 +279,6 @@ class IUserLocalCommunicationService final
|
|
config.node_count_max = info.ldn.node_count_max;
|
|
config.local_communication_version = info.ldn.nodes[0].local_communication_version;
|
|
|
|
- LOG_WARNING(Service_LDN,
|
|
- "(STUBBED) called, intent_id={}/{}, channel={}, node_count_max={}, "
|
|
- "local_communication_version={}",
|
|
- config.intent_id.local_communication_id, config.intent_id.scene_id,
|
|
- config.channel, config.node_count_max, config.local_communication_version);
|
|
-
|
|
IPC::ResponseBuilder rb{ctx, 10};
|
|
rb.Push(rc);
|
|
rb.PushRaw<NetworkConfig>(config);
|
|
@@ -265,17 +297,17 @@ class IUserLocalCommunicationService final
|
|
const std::size_t node_buffer_count = ctx.GetWriteBufferSize(1) / sizeof(NodeLatestUpdate);
|
|
|
|
if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) {
|
|
- LOG_ERROR(Service_LDN, "Invalid buffer size {}, {}", network_buffer_size,
|
|
+ LOG_ERROR(Service_LDN, "Invalid buffer, size = {}, count = {}", network_buffer_size,
|
|
node_buffer_count);
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
rb.Push(ResultBadInput);
|
|
return;
|
|
}
|
|
|
|
- NetworkInfo info;
|
|
+ NetworkInfo info{};
|
|
std::vector<NodeLatestUpdate> latest_update(node_buffer_count);
|
|
|
|
- const auto rc = ResultSuccess;
|
|
+ const auto rc = lan_discovery.GetNetworkInfo(info, latest_update, latest_update.size());
|
|
if (rc.IsError()) {
|
|
LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
@@ -283,9 +315,6 @@ class IUserLocalCommunicationService final
|
|
return;
|
|
}
|
|
|
|
- LOG_WARNING(Service_LDN, "(STUBBED) called, ssid='{}', nodes={}",
|
|
- info.common.ssid.GetStringValue(), info.ldn.node_count);
|
|
-
|
|
ctx.WriteBuffer(info, 0);
|
|
ctx.WriteBuffer(latest_update, 1);
|
|
|
|
@@ -317,92 +346,78 @@ class IUserLocalCommunicationService final
|
|
|
|
u16 count = 0;
|
|
std::vector<NetworkInfo> network_infos(network_info_size);
|
|
+ Result rc = lan_discovery.Scan(network_infos, count, scan_filter);
|
|
|
|
- LOG_WARNING(Service_LDN,
|
|
- "(STUBBED) called, channel={}, filter_scan_flag={}, filter_network_type={}",
|
|
- channel, scan_filter.flag, scan_filter.network_type);
|
|
+ LOG_INFO(Service_LDN,
|
|
+ "called, channel={}, filter_scan_flag={}, filter_network_type={}, is_private={}",
|
|
+ channel, scan_filter.flag, scan_filter.network_type, is_private);
|
|
|
|
ctx.WriteBuffer(network_infos);
|
|
|
|
IPC::ResponseBuilder rb{ctx, 3};
|
|
- rb.Push(ResultSuccess);
|
|
+ rb.Push(rc);
|
|
rb.Push<u32>(count);
|
|
}
|
|
|
|
- void OpenAccessPoint(Kernel::HLERequestContext& ctx) {
|
|
+ void SetWirelessControllerRestriction(Kernel::HLERequestContext& ctx) {
|
|
LOG_WARNING(Service_LDN, "(STUBBED) called");
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
rb.Push(ResultSuccess);
|
|
}
|
|
|
|
+ void OpenAccessPoint(Kernel::HLERequestContext& ctx) {
|
|
+ LOG_INFO(Service_LDN, "called");
|
|
+
|
|
+ IPC::ResponseBuilder rb{ctx, 2};
|
|
+ rb.Push(lan_discovery.OpenAccessPoint());
|
|
+ }
|
|
+
|
|
void CloseAccessPoint(Kernel::HLERequestContext& ctx) {
|
|
- LOG_WARNING(Service_LDN, "(STUBBED) called");
|
|
+ LOG_INFO(Service_LDN, "called");
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
- rb.Push(ResultSuccess);
|
|
+ rb.Push(lan_discovery.CloseAccessPoint());
|
|
}
|
|
|
|
void CreateNetwork(Kernel::HLERequestContext& ctx) {
|
|
- IPC::RequestParser rp{ctx};
|
|
- struct Parameters {
|
|
- SecurityConfig security_config;
|
|
- UserConfig user_config;
|
|
- INSERT_PADDING_WORDS_NOINIT(1);
|
|
- NetworkConfig network_config;
|
|
- };
|
|
- static_assert(sizeof(Parameters) == 0x98, "Parameters has incorrect size.");
|
|
+ LOG_INFO(Service_LDN, "called");
|
|
|
|
- const auto parameters{rp.PopRaw<Parameters>()};
|
|
+ CreateNetworkImpl(ctx);
|
|
+ }
|
|
|
|
- LOG_WARNING(Service_LDN,
|
|
- "(STUBBED) called, passphrase_size={}, security_mode={}, "
|
|
- "local_communication_version={}",
|
|
- parameters.security_config.passphrase_size,
|
|
- parameters.security_config.security_mode,
|
|
- parameters.network_config.local_communication_version);
|
|
+ void CreateNetworkPrivate(Kernel::HLERequestContext& ctx) {
|
|
+ LOG_INFO(Service_LDN, "called");
|
|
|
|
- IPC::ResponseBuilder rb{ctx, 2};
|
|
- rb.Push(ResultSuccess);
|
|
+ CreateNetworkImpl(ctx, true);
|
|
}
|
|
|
|
- void CreateNetworkPrivate(Kernel::HLERequestContext& ctx) {
|
|
+ void CreateNetworkImpl(Kernel::HLERequestContext& ctx, bool is_private = false) {
|
|
IPC::RequestParser rp{ctx};
|
|
- struct Parameters {
|
|
- SecurityConfig security_config;
|
|
- SecurityParameter security_parameter;
|
|
- UserConfig user_config;
|
|
- NetworkConfig network_config;
|
|
- };
|
|
- static_assert(sizeof(Parameters) == 0xB8, "Parameters has incorrect size.");
|
|
-
|
|
- const auto parameters{rp.PopRaw<Parameters>()};
|
|
|
|
- LOG_WARNING(Service_LDN,
|
|
- "(STUBBED) called, passphrase_size={}, security_mode={}, "
|
|
- "local_communication_version={}",
|
|
- parameters.security_config.passphrase_size,
|
|
- parameters.security_config.security_mode,
|
|
- parameters.network_config.local_communication_version);
|
|
+ const auto security_config{rp.PopRaw<SecurityConfig>()};
|
|
+ [[maybe_unused]] const auto security_parameter{is_private ? rp.PopRaw<SecurityParameter>()
|
|
+ : SecurityParameter{}};
|
|
+ const auto user_config{rp.PopRaw<UserConfig>()};
|
|
+ rp.Pop<u32>(); // Padding
|
|
+ const auto network_Config{rp.PopRaw<NetworkConfig>()};
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
- rb.Push(ResultSuccess);
|
|
+ rb.Push(lan_discovery.CreateNetwork(security_config, user_config, network_Config));
|
|
}
|
|
|
|
void DestroyNetwork(Kernel::HLERequestContext& ctx) {
|
|
- LOG_WARNING(Service_LDN, "(STUBBED) called");
|
|
+ LOG_INFO(Service_LDN, "called");
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
- rb.Push(ResultSuccess);
|
|
+ rb.Push(lan_discovery.DestroyNetwork());
|
|
}
|
|
|
|
void SetAdvertiseData(Kernel::HLERequestContext& ctx) {
|
|
std::vector<u8> read_buffer = ctx.ReadBuffer();
|
|
|
|
- LOG_WARNING(Service_LDN, "(STUBBED) called, size {}", read_buffer.size());
|
|
-
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
- rb.Push(ResultSuccess);
|
|
+ rb.Push(lan_discovery.SetAdvertiseData(read_buffer));
|
|
}
|
|
|
|
void SetStationAcceptPolicy(Kernel::HLERequestContext& ctx) {
|
|
@@ -420,17 +435,17 @@ class IUserLocalCommunicationService final
|
|
}
|
|
|
|
void OpenStation(Kernel::HLERequestContext& ctx) {
|
|
- LOG_WARNING(Service_LDN, "(STUBBED) called");
|
|
+ LOG_INFO(Service_LDN, "called");
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
- rb.Push(ResultSuccess);
|
|
+ rb.Push(lan_discovery.OpenStation());
|
|
}
|
|
|
|
void CloseStation(Kernel::HLERequestContext& ctx) {
|
|
- LOG_WARNING(Service_LDN, "(STUBBED) called");
|
|
+ LOG_INFO(Service_LDN, "called");
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
- rb.Push(ResultSuccess);
|
|
+ rb.Push(lan_discovery.CloseStation());
|
|
}
|
|
|
|
void Connect(Kernel::HLERequestContext& ctx) {
|
|
@@ -445,16 +460,13 @@ class IUserLocalCommunicationService final
|
|
|
|
const auto parameters{rp.PopRaw<Parameters>()};
|
|
|
|
- LOG_WARNING(Service_LDN,
|
|
- "(STUBBED) called, passphrase_size={}, security_mode={}, "
|
|
- "local_communication_version={}",
|
|
- parameters.security_config.passphrase_size,
|
|
- parameters.security_config.security_mode,
|
|
- parameters.local_communication_version);
|
|
+ LOG_INFO(Service_LDN,
|
|
+ "called, passphrase_size={}, security_mode={}, "
|
|
+ "local_communication_version={}",
|
|
+ parameters.security_config.passphrase_size,
|
|
+ parameters.security_config.security_mode, parameters.local_communication_version);
|
|
|
|
const std::vector<u8> read_buffer = ctx.ReadBuffer();
|
|
- NetworkInfo network_info{};
|
|
-
|
|
if (read_buffer.size() != sizeof(NetworkInfo)) {
|
|
LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!");
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
@@ -462,40 +474,47 @@ class IUserLocalCommunicationService final
|
|
return;
|
|
}
|
|
|
|
+ NetworkInfo network_info{};
|
|
std::memcpy(&network_info, read_buffer.data(), read_buffer.size());
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
- rb.Push(ResultSuccess);
|
|
+ rb.Push(lan_discovery.Connect(network_info, parameters.user_config,
|
|
+ static_cast<u16>(parameters.local_communication_version)));
|
|
}
|
|
|
|
void Disconnect(Kernel::HLERequestContext& ctx) {
|
|
- LOG_WARNING(Service_LDN, "(STUBBED) called");
|
|
+ LOG_INFO(Service_LDN, "called");
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
- rb.Push(ResultSuccess);
|
|
+ rb.Push(lan_discovery.Disconnect());
|
|
}
|
|
- void Initialize(Kernel::HLERequestContext& ctx) {
|
|
- LOG_WARNING(Service_LDN, "(STUBBED) called");
|
|
|
|
+ void Initialize(Kernel::HLERequestContext& ctx) {
|
|
const auto rc = InitializeImpl(ctx);
|
|
+ if (rc.IsError()) {
|
|
+ LOG_ERROR(Service_LDN, "Network isn't initialized, rc={}", rc.raw);
|
|
+ }
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
rb.Push(rc);
|
|
}
|
|
|
|
void Finalize(Kernel::HLERequestContext& ctx) {
|
|
- LOG_WARNING(Service_LDN, "(STUBBED) called");
|
|
+ if (auto room_member = room_network.GetRoomMember().lock()) {
|
|
+ room_member->Unbind(ldn_packet_received);
|
|
+ }
|
|
|
|
is_initialized = false;
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
- rb.Push(ResultSuccess);
|
|
+ rb.Push(lan_discovery.Finalize());
|
|
}
|
|
|
|
void Initialize2(Kernel::HLERequestContext& ctx) {
|
|
- LOG_WARNING(Service_LDN, "(STUBBED) called");
|
|
-
|
|
const auto rc = InitializeImpl(ctx);
|
|
+ if (rc.IsError()) {
|
|
+ LOG_ERROR(Service_LDN, "Network isn't initialized, rc={}", rc.raw);
|
|
+ }
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
rb.Push(rc);
|
|
@@ -508,14 +527,26 @@ class IUserLocalCommunicationService final
|
|
return ResultAirplaneModeEnabled;
|
|
}
|
|
|
|
+ if (auto room_member = room_network.GetRoomMember().lock()) {
|
|
+ ldn_packet_received = room_member->BindOnLdnPacketReceived(
|
|
+ [this](const Network::LDNPacket& packet) { OnLDNPacketReceived(packet); });
|
|
+ } else {
|
|
+ LOG_ERROR(Service_LDN, "Couldn't bind callback!");
|
|
+ return ResultAirplaneModeEnabled;
|
|
+ }
|
|
+
|
|
+ lan_discovery.Initialize([&]() { OnEventFired(); });
|
|
is_initialized = true;
|
|
- // TODO (flTobi): Change this to ResultSuccess when LDN is fully implemented
|
|
- return ResultAirplaneModeEnabled;
|
|
+ return ResultSuccess;
|
|
}
|
|
|
|
KernelHelpers::ServiceContext service_context;
|
|
Kernel::KEvent* state_change_event;
|
|
Network::RoomNetwork& room_network;
|
|
+ LANDiscovery lan_discovery;
|
|
+
|
|
+ // Callback identifier for the OnLDNPacketReceived event.
|
|
+ Network::RoomMember::CallbackHandle<Network::LDNPacket> ldn_packet_received;
|
|
|
|
bool is_initialized{};
|
|
};
|
|
diff --git a/src/core/hle/service/ldn/ldn_types.h b/src/core/hle/service/ldn/ldn_types.h
|
|
index 6231e936dade..d6609fff55fd 100644
|
|
--- a/src/core/hle/service/ldn/ldn_types.h
|
|
+++ b/src/core/hle/service/ldn/ldn_types.h
|
|
@@ -31,6 +31,14 @@ enum class NodeStateChange : u8 {
|
|
DisconnectAndConnect,
|
|
};
|
|
|
|
+inline NodeStateChange operator|(NodeStateChange a, NodeStateChange b) {
|
|
+ return static_cast<NodeStateChange>(static_cast<u8>(a) | static_cast<u8>(b));
|
|
+}
|
|
+
|
|
+inline NodeStateChange operator|=(NodeStateChange& a, NodeStateChange b) {
|
|
+ return a = a | b;
|
|
+}
|
|
+
|
|
enum class ScanFilterFlag : u32 {
|
|
None = 0,
|
|
LocalCommunicationId = 1 << 0,
|
|
@@ -100,13 +108,13 @@ enum class AcceptPolicy : u8 {
|
|
|
|
enum class WifiChannel : s16 {
|
|
Default = 0,
|
|
- wifi24_1 = 1,
|
|
- wifi24_6 = 6,
|
|
- wifi24_11 = 11,
|
|
- wifi50_36 = 36,
|
|
- wifi50_40 = 40,
|
|
- wifi50_44 = 44,
|
|
- wifi50_48 = 48,
|
|
+ Wifi24_1 = 1,
|
|
+ Wifi24_6 = 6,
|
|
+ Wifi24_11 = 11,
|
|
+ Wifi50_36 = 36,
|
|
+ Wifi50_40 = 40,
|
|
+ Wifi50_44 = 44,
|
|
+ Wifi50_48 = 48,
|
|
};
|
|
|
|
enum class LinkLevel : s8 {
|
|
@@ -116,6 +124,11 @@ enum class LinkLevel : s8 {
|
|
Excellent,
|
|
};
|
|
|
|
+enum class NodeStatus : u8 {
|
|
+ Disconnected,
|
|
+ Connected,
|
|
+};
|
|
+
|
|
struct NodeLatestUpdate {
|
|
NodeStateChange state_change;
|
|
INSERT_PADDING_BYTES(0x7); // Unknown
|
|
@@ -159,19 +172,14 @@ struct Ssid {
|
|
std::string GetStringValue() const {
|
|
return std::string(raw.data());
|
|
}
|
|
-};
|
|
-static_assert(sizeof(Ssid) == 0x22, "Ssid is an invalid size");
|
|
-
|
|
-struct Ipv4Address {
|
|
- union {
|
|
- u32 raw{};
|
|
- std::array<u8, 4> bytes;
|
|
- };
|
|
|
|
- std::string GetStringValue() const {
|
|
- return fmt::format("{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]);
|
|
+ bool operator==(const Ssid& b) const {
|
|
+ return (length == b.length) && (std::memcmp(raw.data(), b.raw.data(), length) == 0);
|
|
}
|
|
};
|
|
+static_assert(sizeof(Ssid) == 0x22, "Ssid is an invalid size");
|
|
+
|
|
+using Ipv4Address = std::array<u8, 4>;
|
|
static_assert(sizeof(Ipv4Address) == 0x4, "Ipv4Address is an invalid size");
|
|
|
|
struct MacAddress {
|
|
@@ -181,6 +189,14 @@ struct MacAddress {
|
|
};
|
|
static_assert(sizeof(MacAddress) == 0x6, "MacAddress is an invalid size");
|
|
|
|
+struct MACAddressHash {
|
|
+ size_t operator()(const MacAddress& address) const {
|
|
+ u64 value{};
|
|
+ std::memcpy(&value, address.raw.data(), sizeof(address.raw));
|
|
+ return value;
|
|
+ }
|
|
+};
|
|
+
|
|
struct ScanFilter {
|
|
NetworkId network_id;
|
|
NetworkType network_type;
|
|
diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp
|
|
index 0c746bd82427..7d5d37bbcd7f 100644
|
|
--- a/src/core/internal_network/socket_proxy.cpp
|
|
+++ b/src/core/internal_network/socket_proxy.cpp
|
|
@@ -6,6 +6,7 @@
|
|
|
|
#include "common/assert.h"
|
|
#include "common/logging/log.h"
|
|
+#include "common/zstd_compression.h"
|
|
#include "core/internal_network/network.h"
|
|
#include "core/internal_network/network_interface.h"
|
|
#include "core/internal_network/socket_proxy.h"
|
|
@@ -32,8 +33,11 @@ void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) {
|
|
return;
|
|
}
|
|
|
|
+ auto decompressed = packet;
|
|
+ decompressed.data = Common::Compression::DecompressDataZSTD(packet.data);
|
|
+
|
|
std::lock_guard guard(packets_mutex);
|
|
- received_packets.push(packet);
|
|
+ received_packets.push(decompressed);
|
|
}
|
|
|
|
template <typename T>
|
|
@@ -185,6 +189,8 @@ std::pair<s32, Errno> ProxySocket::Send(const std::vector<u8>& message, int flag
|
|
void ProxySocket::SendPacket(ProxyPacket& packet) {
|
|
if (auto room_member = room_network.GetRoomMember().lock()) {
|
|
if (room_member->IsConnected()) {
|
|
+ packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(),
|
|
+ packet.data.size());
|
|
room_member->SendProxyPacket(packet);
|
|
}
|
|
}
|
|
diff --git a/src/dedicated_room/yuzu_room.cpp b/src/dedicated_room/yuzu_room.cpp
|
|
index 7b6deba417fd..8d8ac1ed74f1 100644
|
|
--- a/src/dedicated_room/yuzu_room.cpp
|
|
+++ b/src/dedicated_room/yuzu_room.cpp
|
|
@@ -76,7 +76,8 @@ static constexpr char BanListMagic[] = "YuzuRoom-BanList-1";
|
|
static constexpr char token_delimiter{':'};
|
|
|
|
static void PadToken(std::string& token) {
|
|
- while (token.size() % 4 != 0) {
|
|
+ const auto remainder = token.size() % 3;
|
|
+ for (size_t i = 0; i < (3 - remainder); i++) {
|
|
token.push_back('=');
|
|
}
|
|
}
|
|
diff --git a/src/network/room.cpp b/src/network/room.cpp
|
|
index 8c63b255bc29..dc5dbce7fa95 100644
|
|
--- a/src/network/room.cpp
|
|
+++ b/src/network/room.cpp
|
|
@@ -211,6 +211,12 @@ class Room::RoomImpl {
|
|
*/
|
|
void HandleProxyPacket(const ENetEvent* event);
|
|
|
|
+ /**
|
|
+ * Broadcasts this packet to all members except the sender.
|
|
+ * @param event The ENet event containing the data
|
|
+ */
|
|
+ void HandleLdnPacket(const ENetEvent* event);
|
|
+
|
|
/**
|
|
* Extracts a chat entry from a received ENet packet and adds it to the chat queue.
|
|
* @param event The ENet event that was received.
|
|
@@ -247,6 +253,9 @@ void Room::RoomImpl::ServerLoop() {
|
|
case IdProxyPacket:
|
|
HandleProxyPacket(&event);
|
|
break;
|
|
+ case IdLdnPacket:
|
|
+ HandleLdnPacket(&event);
|
|
+ break;
|
|
case IdChatMessage:
|
|
HandleChatPacket(&event);
|
|
break;
|
|
@@ -861,6 +870,60 @@ void Room::RoomImpl::HandleProxyPacket(const ENetEvent* event) {
|
|
enet_host_flush(server);
|
|
}
|
|
|
|
+void Room::RoomImpl::HandleLdnPacket(const ENetEvent* event) {
|
|
+ Packet in_packet;
|
|
+ in_packet.Append(event->packet->data, event->packet->dataLength);
|
|
+
|
|
+ in_packet.IgnoreBytes(sizeof(u8)); // Message type
|
|
+
|
|
+ in_packet.IgnoreBytes(sizeof(u8)); // LAN packet type
|
|
+ in_packet.IgnoreBytes(sizeof(IPv4Address)); // Local IP
|
|
+
|
|
+ IPv4Address remote_ip;
|
|
+ in_packet.Read(remote_ip); // Remote IP
|
|
+
|
|
+ bool broadcast;
|
|
+ in_packet.Read(broadcast); // Broadcast
|
|
+
|
|
+ Packet out_packet;
|
|
+ out_packet.Append(event->packet->data, event->packet->dataLength);
|
|
+ ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(),
|
|
+ ENET_PACKET_FLAG_RELIABLE);
|
|
+
|
|
+ const auto& destination_address = remote_ip;
|
|
+ if (broadcast) { // Send the data to everyone except the sender
|
|
+ std::lock_guard lock(member_mutex);
|
|
+ bool sent_packet = false;
|
|
+ for (const auto& member : members) {
|
|
+ if (member.peer != event->peer) {
|
|
+ sent_packet = true;
|
|
+ enet_peer_send(member.peer, 0, enet_packet);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!sent_packet) {
|
|
+ enet_packet_destroy(enet_packet);
|
|
+ }
|
|
+ } else {
|
|
+ std::lock_guard lock(member_mutex);
|
|
+ auto member = std::find_if(members.begin(), members.end(),
|
|
+ [destination_address](const Member& member_entry) -> bool {
|
|
+ return member_entry.fake_ip == destination_address;
|
|
+ });
|
|
+ if (member != members.end()) {
|
|
+ enet_peer_send(member->peer, 0, enet_packet);
|
|
+ } else {
|
|
+ LOG_ERROR(Network,
|
|
+ "Attempting to send to unknown IP address: "
|
|
+ "{}.{}.{}.{}",
|
|
+ destination_address[0], destination_address[1], destination_address[2],
|
|
+ destination_address[3]);
|
|
+ enet_packet_destroy(enet_packet);
|
|
+ }
|
|
+ }
|
|
+ enet_host_flush(server);
|
|
+}
|
|
+
|
|
void Room::RoomImpl::HandleChatPacket(const ENetEvent* event) {
|
|
Packet in_packet;
|
|
in_packet.Append(event->packet->data, event->packet->dataLength);
|
|
diff --git a/src/network/room.h b/src/network/room.h
|
|
index c2a4b1a70240..edbd3ecfb23f 100644
|
|
--- a/src/network/room.h
|
|
+++ b/src/network/room.h
|
|
@@ -40,6 +40,7 @@ enum RoomMessageTypes : u8 {
|
|
IdRoomInformation,
|
|
IdSetGameInfo,
|
|
IdProxyPacket,
|
|
+ IdLdnPacket,
|
|
IdChatMessage,
|
|
IdNameCollision,
|
|
IdIpCollision,
|
|
diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp
|
|
index 06818af783c7..572e55a5b83c 100644
|
|
--- a/src/network/room_member.cpp
|
|
+++ b/src/network/room_member.cpp
|
|
@@ -58,6 +58,7 @@ class RoomMember::RoomMemberImpl {
|
|
|
|
private:
|
|
CallbackSet<ProxyPacket> callback_set_proxy_packet;
|
|
+ CallbackSet<LDNPacket> callback_set_ldn_packet;
|
|
CallbackSet<ChatEntry> callback_set_chat_messages;
|
|
CallbackSet<StatusMessageEntry> callback_set_status_messages;
|
|
CallbackSet<RoomInformation> callback_set_room_information;
|
|
@@ -107,6 +108,12 @@ class RoomMember::RoomMemberImpl {
|
|
*/
|
|
void HandleProxyPackets(const ENetEvent* event);
|
|
|
|
+ /**
|
|
+ * Extracts an LdnPacket from a received ENet packet.
|
|
+ * @param event The ENet event that was received.
|
|
+ */
|
|
+ void HandleLdnPackets(const ENetEvent* event);
|
|
+
|
|
/**
|
|
* Extracts a chat entry from a received ENet packet and adds it to the chat queue.
|
|
* @param event The ENet event that was received.
|
|
@@ -166,6 +173,9 @@ void RoomMember::RoomMemberImpl::MemberLoop() {
|
|
case IdProxyPacket:
|
|
HandleProxyPackets(&event);
|
|
break;
|
|
+ case IdLdnPacket:
|
|
+ HandleLdnPackets(&event);
|
|
+ break;
|
|
case IdChatMessage:
|
|
HandleChatPacket(&event);
|
|
break;
|
|
@@ -372,6 +382,27 @@ void RoomMember::RoomMemberImpl::HandleProxyPackets(const ENetEvent* event) {
|
|
Invoke<ProxyPacket>(proxy_packet);
|
|
}
|
|
|
|
+void RoomMember::RoomMemberImpl::HandleLdnPackets(const ENetEvent* event) {
|
|
+ LDNPacket ldn_packet{};
|
|
+ Packet packet;
|
|
+ packet.Append(event->packet->data, event->packet->dataLength);
|
|
+
|
|
+ // Ignore the first byte, which is the message id.
|
|
+ packet.IgnoreBytes(sizeof(u8)); // Ignore the message type
|
|
+
|
|
+ u8 packet_type;
|
|
+ packet.Read(packet_type);
|
|
+ ldn_packet.type = static_cast<LDNPacketType>(packet_type);
|
|
+
|
|
+ packet.Read(ldn_packet.local_ip);
|
|
+ packet.Read(ldn_packet.remote_ip);
|
|
+ packet.Read(ldn_packet.broadcast);
|
|
+
|
|
+ packet.Read(ldn_packet.data);
|
|
+
|
|
+ Invoke<LDNPacket>(ldn_packet);
|
|
+}
|
|
+
|
|
void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) {
|
|
Packet packet;
|
|
packet.Append(event->packet->data, event->packet->dataLength);
|
|
@@ -449,6 +480,11 @@ RoomMember::RoomMemberImpl::CallbackSet<ProxyPacket>& RoomMember::RoomMemberImpl
|
|
return callback_set_proxy_packet;
|
|
}
|
|
|
|
+template <>
|
|
+RoomMember::RoomMemberImpl::CallbackSet<LDNPacket>& RoomMember::RoomMemberImpl::Callbacks::Get() {
|
|
+ return callback_set_ldn_packet;
|
|
+}
|
|
+
|
|
template <>
|
|
RoomMember::RoomMemberImpl::CallbackSet<RoomMember::State>&
|
|
RoomMember::RoomMemberImpl::Callbacks::Get() {
|
|
@@ -607,6 +643,21 @@ void RoomMember::SendProxyPacket(const ProxyPacket& proxy_packet) {
|
|
room_member_impl->Send(std::move(packet));
|
|
}
|
|
|
|
+void RoomMember::SendLdnPacket(const LDNPacket& ldn_packet) {
|
|
+ Packet packet;
|
|
+ packet.Write(static_cast<u8>(IdLdnPacket));
|
|
+
|
|
+ packet.Write(static_cast<u8>(ldn_packet.type));
|
|
+
|
|
+ packet.Write(ldn_packet.local_ip);
|
|
+ packet.Write(ldn_packet.remote_ip);
|
|
+ packet.Write(ldn_packet.broadcast);
|
|
+
|
|
+ packet.Write(ldn_packet.data);
|
|
+
|
|
+ room_member_impl->Send(std::move(packet));
|
|
+}
|
|
+
|
|
void RoomMember::SendChatMessage(const std::string& message) {
|
|
Packet packet;
|
|
packet.Write(static_cast<u8>(IdChatMessage));
|
|
@@ -663,6 +714,11 @@ RoomMember::CallbackHandle<ProxyPacket> RoomMember::BindOnProxyPacketReceived(
|
|
return room_member_impl->Bind(callback);
|
|
}
|
|
|
|
+RoomMember::CallbackHandle<LDNPacket> RoomMember::BindOnLdnPacketReceived(
|
|
+ std::function<void(const LDNPacket&)> callback) {
|
|
+ return room_member_impl->Bind(callback);
|
|
+}
|
|
+
|
|
RoomMember::CallbackHandle<RoomInformation> RoomMember::BindOnRoomInformationChanged(
|
|
std::function<void(const RoomInformation&)> callback) {
|
|
return room_member_impl->Bind(callback);
|
|
@@ -699,6 +755,7 @@ void RoomMember::Leave() {
|
|
}
|
|
|
|
template void RoomMember::Unbind(CallbackHandle<ProxyPacket>);
|
|
+template void RoomMember::Unbind(CallbackHandle<LDNPacket>);
|
|
template void RoomMember::Unbind(CallbackHandle<RoomMember::State>);
|
|
template void RoomMember::Unbind(CallbackHandle<RoomMember::Error>);
|
|
template void RoomMember::Unbind(CallbackHandle<RoomInformation>);
|
|
diff --git a/src/network/room_member.h b/src/network/room_member.h
|
|
index f578f7f6a31a..0d6417294579 100644
|
|
--- a/src/network/room_member.h
|
|
+++ b/src/network/room_member.h
|
|
@@ -17,7 +17,24 @@ namespace Network {
|
|
using AnnounceMultiplayerRoom::GameInfo;
|
|
using AnnounceMultiplayerRoom::RoomInformation;
|
|
|
|
-/// Information about the received WiFi packets.
|
|
+enum class LDNPacketType : u8 {
|
|
+ Scan,
|
|
+ ScanResp,
|
|
+ Connect,
|
|
+ SyncNetwork,
|
|
+ Disconnect,
|
|
+ DestroyNetwork,
|
|
+};
|
|
+
|
|
+struct LDNPacket {
|
|
+ LDNPacketType type;
|
|
+ IPv4Address local_ip;
|
|
+ IPv4Address remote_ip;
|
|
+ bool broadcast;
|
|
+ std::vector<u8> data;
|
|
+};
|
|
+
|
|
+/// Information about the received proxy packets.
|
|
struct ProxyPacket {
|
|
SockAddrIn local_endpoint;
|
|
SockAddrIn remote_endpoint;
|
|
@@ -151,6 +168,12 @@ class RoomMember final {
|
|
*/
|
|
void SendProxyPacket(const ProxyPacket& packet);
|
|
|
|
+ /**
|
|
+ * Sends an LDN packet to the room.
|
|
+ * @param packet The WiFi packet to send.
|
|
+ */
|
|
+ void SendLdnPacket(const LDNPacket& packet);
|
|
+
|
|
/**
|
|
* Sends a chat message to the room.
|
|
* @param message The contents of the message.
|
|
@@ -204,6 +227,16 @@ class RoomMember final {
|
|
CallbackHandle<ProxyPacket> BindOnProxyPacketReceived(
|
|
std::function<void(const ProxyPacket&)> callback);
|
|
|
|
+ /**
|
|
+ * Binds a function to an event that will be triggered every time an LDNPacket is received.
|
|
+ * The function wil be called everytime the event is triggered.
|
|
+ * The callback function must not bind or unbind a function. Doing so will cause a deadlock
|
|
+ * @param callback The function to call
|
|
+ * @return A handle used for removing the function from the registered list
|
|
+ */
|
|
+ CallbackHandle<LDNPacket> BindOnLdnPacketReceived(
|
|
+ std::function<void(const LDNPacket&)> callback);
|
|
+
|
|
/**
|
|
* Binds a function to an event that will be triggered every time the RoomInformation changes.
|
|
* The function wil be called every time the event is triggered.
|
|
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
|
|
index a85adc072429..9dfa8d639c26 100644
|
|
--- a/src/yuzu/main.cpp
|
|
+++ b/src/yuzu/main.cpp
|
|
@@ -896,8 +896,8 @@ void GMainWindow::InitializeWidgets() {
|
|
}
|
|
|
|
// TODO (flTobi): Add the widget when multiplayer is fully implemented
|
|
- // statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0);
|
|
- // statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0);
|
|
+ statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0);
|
|
+ statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0);
|
|
|
|
tas_label = new QLabel();
|
|
tas_label->setObjectName(QStringLiteral("TASlabel"));
|
|
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
|
|
index cdf31b417d75..60a8deab1c9a 100644
|
|
--- a/src/yuzu/main.ui
|
|
+++ b/src/yuzu/main.ui
|
|
@@ -120,6 +120,20 @@
|
|
<addaction name="menu_Reset_Window_Size"/>
|
|
<addaction name="menu_View_Debugging"/>
|
|
</widget>
|
|
+ <widget class="QMenu" name="menu_Multiplayer">
|
|
+ <property name="enabled">
|
|
+ <bool>true</bool>
|
|
+ </property>
|
|
+ <property name="title">
|
|
+ <string>Multiplayer</string>
|
|
+ </property>
|
|
+ <addaction name="action_View_Lobby"/>
|
|
+ <addaction name="action_Start_Room"/>
|
|
+ <addaction name="action_Connect_To_Room"/>
|
|
+ <addaction name="separator"/>
|
|
+ <addaction name="action_Show_Room"/>
|
|
+ <addaction name="action_Leave_Room"/>
|
|
+ </widget>
|
|
<widget class="QMenu" name="menu_Tools">
|
|
<property name="title">
|
|
<string>&Tools</string>
|
|
diff --git a/src/yuzu/multiplayer/chat_room.cpp b/src/yuzu/multiplayer/chat_room.cpp
|
|
index 9e672f82e98d..dec9696c186e 100644
|
|
--- a/src/yuzu/multiplayer/chat_room.cpp
|
|
+++ b/src/yuzu/multiplayer/chat_room.cpp
|
|
@@ -61,7 +61,10 @@ class ChatMessage {
|
|
|
|
/// Format the message using the players color
|
|
QString GetPlayerChatMessage(u16 player) const {
|
|
- auto color = player_color[player % 16];
|
|
+ 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;
|
|
@@ -84,9 +87,12 @@ class ChatMessage {
|
|
}
|
|
|
|
private:
|
|
- static constexpr std::array<const char*, 16> player_color = {
|
|
+ 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"}};
|
|
+ "#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;
|
|
diff --git a/src/yuzu/multiplayer/state.cpp b/src/yuzu/multiplayer/state.cpp
|
|
index 66e098296d02..3ad8460281db 100644
|
|
--- a/src/yuzu/multiplayer/state.cpp
|
|
+++ b/src/yuzu/multiplayer/state.cpp
|
|
@@ -249,6 +249,7 @@ void MultiplayerState::ShowNotification() {
|
|
return; // Do not show notification if the chat window currently has focus
|
|
show_notification = true;
|
|
QApplication::alert(nullptr);
|
|
+ QApplication::beep();
|
|
status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected_notification")).pixmap(16));
|
|
status_text->setText(tr("New Messages Received"));
|
|
}
|
|
|
|
From 8f207bd93ddb9778f0242fca0dab6ef155bd2a97 Mon Sep 17 00:00:00 2001
|
|
From: german77 <juangerman-13@hotmail.com>
|
|
Date: Fri, 9 Sep 2022 15:29:22 -0500
|
|
Subject: [PATCH 2/5] yuzu: Multiple room UI improvements
|
|
|
|
---
|
|
src/core/hle/service/hid/hid.cpp | 3 +-
|
|
.../internal_network/network_interface.cpp | 10 +++
|
|
src/core/internal_network/network_interface.h | 1 +
|
|
src/yuzu/main.cpp | 8 ++
|
|
src/yuzu/main.h | 1 +
|
|
src/yuzu/main.ui | 10 +--
|
|
src/yuzu/multiplayer/client_room.cpp | 3 +-
|
|
src/yuzu/multiplayer/direct_connect.cpp | 2 +
|
|
src/yuzu/multiplayer/direct_connect.h | 1 +
|
|
src/yuzu/multiplayer/host_room.cpp | 1 +
|
|
src/yuzu/multiplayer/host_room.h | 3 +
|
|
src/yuzu/multiplayer/lobby.cpp | 67 +++++++++++-----
|
|
src/yuzu/multiplayer/lobby.h | 8 ++
|
|
src/yuzu/multiplayer/lobby_p.h | 16 ++--
|
|
src/yuzu/multiplayer/message.cpp | 6 +-
|
|
src/yuzu/multiplayer/state.cpp | 79 +++++++++++++------
|
|
src/yuzu/multiplayer/state.h | 14 ++++
|
|
src/yuzu/uisettings.h | 2 +-
|
|
18 files changed, 176 insertions(+), 59 deletions(-)
|
|
|
|
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
|
|
index 3d3457160030..7e923462baf5 100644
|
|
--- a/src/core/hle/service/hid/hid.cpp
|
|
+++ b/src/core/hle/service/hid/hid.cpp
|
|
@@ -35,7 +35,8 @@ namespace Service::HID {
|
|
|
|
// Updating period for each HID device.
|
|
// Period time is obtained by measuring the number of samples in a second on HW using a homebrew
|
|
-constexpr auto pad_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 250Hz)
|
|
+// Correct pad_update_ns is 4ms this is overclocked to lower input lag
|
|
+constexpr auto pad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz)
|
|
constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
|
|
constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
|
|
|
|
diff --git a/src/core/internal_network/network_interface.cpp b/src/core/internal_network/network_interface.cpp
|
|
index 0f0a661606c8..858ae1cfbbb2 100644
|
|
--- a/src/core/internal_network/network_interface.cpp
|
|
+++ b/src/core/internal_network/network_interface.cpp
|
|
@@ -206,4 +206,14 @@ std::optional<NetworkInterface> GetSelectedNetworkInterface() {
|
|
return *res;
|
|
}
|
|
|
|
+void SelectFirstNetworkInterface() {
|
|
+ const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
|
|
+
|
|
+ if (network_interfaces.size() == 0) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ Settings::values.network_interface.SetValue(network_interfaces[0].name);
|
|
+}
|
|
+
|
|
} // namespace Network
|
|
diff --git a/src/core/internal_network/network_interface.h b/src/core/internal_network/network_interface.h
|
|
index 9b98b6b4204d..175e61b1f9f6 100644
|
|
--- a/src/core/internal_network/network_interface.h
|
|
+++ b/src/core/internal_network/network_interface.h
|
|
@@ -24,5 +24,6 @@ struct NetworkInterface {
|
|
|
|
std::vector<NetworkInterface> GetAvailableNetworkInterfaces();
|
|
std::optional<NetworkInterface> GetSelectedNetworkInterface();
|
|
+void SelectFirstNetworkInterface();
|
|
|
|
} // namespace Network
|
|
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
|
|
index 9dfa8d639c26..deee1c370fb9 100644
|
|
--- a/src/yuzu/main.cpp
|
|
+++ b/src/yuzu/main.cpp
|
|
@@ -1296,6 +1296,7 @@ void GMainWindow::ConnectMenuEvents() {
|
|
&MultiplayerState::OnDirectConnectToRoom);
|
|
connect(ui->action_Show_Room, &QAction::triggered, multiplayer_state,
|
|
&MultiplayerState::OnOpenNetworkRoom);
|
|
+ connect(multiplayer_state, &MultiplayerState::SaveConfig, this, &GMainWindow::OnSaveConfig);
|
|
|
|
// Tools
|
|
connect_menu(ui->action_Rederive, std::bind(&GMainWindow::OnReinitializeKeys, this,
|
|
@@ -1336,6 +1337,8 @@ void GMainWindow::UpdateMenuState() {
|
|
} else {
|
|
ui->action_Pause->setText(tr("&Pause"));
|
|
}
|
|
+
|
|
+ multiplayer_state->UpdateNotificationStatus();
|
|
}
|
|
|
|
void GMainWindow::OnDisplayTitleBars(bool show) {
|
|
@@ -2766,6 +2769,11 @@ void GMainWindow::OnExit() {
|
|
OnStopGame();
|
|
}
|
|
|
|
+void GMainWindow::OnSaveConfig() {
|
|
+ system->ApplySettings();
|
|
+ config->Save();
|
|
+}
|
|
+
|
|
void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) {
|
|
OverlayDialog dialog(render_window, *system, error_code, error_text, QString{}, tr("OK"),
|
|
Qt::AlignLeft | Qt::AlignVCenter);
|
|
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
|
|
index 1ae2b93d9b23..1a756b171e33 100644
|
|
--- a/src/yuzu/main.h
|
|
+++ b/src/yuzu/main.h
|
|
@@ -169,6 +169,7 @@ public slots:
|
|
void OnLoadComplete();
|
|
void OnExecuteProgram(std::size_t program_index);
|
|
void OnExit();
|
|
+ void OnSaveConfig();
|
|
void ControllerSelectorReconfigureControllers(
|
|
const Core::Frontend::ControllerParameters& parameters);
|
|
void SoftwareKeyboardInitialize(
|
|
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
|
|
index 60a8deab1c9a..de1545216a81 100644
|
|
--- a/src/yuzu/main.ui
|
|
+++ b/src/yuzu/main.ui
|
|
@@ -265,7 +265,7 @@
|
|
<bool>true</bool>
|
|
</property>
|
|
<property name="text">
|
|
- <string>Browse Public Game Lobby</string>
|
|
+ <string>&Browse Public Game Lobby</string>
|
|
</property>
|
|
</action>
|
|
<action name="action_Start_Room">
|
|
@@ -273,7 +273,7 @@
|
|
<bool>true</bool>
|
|
</property>
|
|
<property name="text">
|
|
- <string>Create Room</string>
|
|
+ <string>&Create Room</string>
|
|
</property>
|
|
</action>
|
|
<action name="action_Leave_Room">
|
|
@@ -281,12 +281,12 @@
|
|
<bool>false</bool>
|
|
</property>
|
|
<property name="text">
|
|
- <string>Leave Room</string>
|
|
+ <string>&Leave Room</string>
|
|
</property>
|
|
</action>
|
|
<action name="action_Connect_To_Room">
|
|
<property name="text">
|
|
- <string>Direct Connect to Room</string>
|
|
+ <string>&Direct Connect to Room</string>
|
|
</property>
|
|
</action>
|
|
<action name="action_Show_Room">
|
|
@@ -294,7 +294,7 @@
|
|
<bool>false</bool>
|
|
</property>
|
|
<property name="text">
|
|
- <string>Show Current Room</string>
|
|
+ <string>&Show Current Room</string>
|
|
</property>
|
|
</action>
|
|
<action name="action_Fullscreen">
|
|
diff --git a/src/yuzu/multiplayer/client_room.cpp b/src/yuzu/multiplayer/client_room.cpp
|
|
index b34a8d004abf..caf34a414fc6 100644
|
|
--- a/src/yuzu/multiplayer/client_room.cpp
|
|
+++ b/src/yuzu/multiplayer/client_room.cpp
|
|
@@ -97,8 +97,9 @@ void ClientRoomWindow::UpdateView() {
|
|
auto memberlist = member->GetMemberInformation();
|
|
ui->chat->SetPlayerList(memberlist);
|
|
const auto information = member->GetRoomInformation();
|
|
- setWindowTitle(QString(tr("%1 (%2/%3 members) - connected"))
|
|
+ setWindowTitle(QString(tr("%1 - %2 (%3/%4 members) - connected"))
|
|
.arg(QString::fromStdString(information.name))
|
|
+ .arg(QString::fromStdString(information.preferred_game.name))
|
|
.arg(memberlist.size())
|
|
.arg(information.member_slots));
|
|
ui->description->setText(QString::fromStdString(information.description));
|
|
diff --git a/src/yuzu/multiplayer/direct_connect.cpp b/src/yuzu/multiplayer/direct_connect.cpp
|
|
index 017063074479..10bf0a4fb3d3 100644
|
|
--- a/src/yuzu/multiplayer/direct_connect.cpp
|
|
+++ b/src/yuzu/multiplayer/direct_connect.cpp
|
|
@@ -106,6 +106,8 @@ void DirectConnectWindow::Connect() {
|
|
UISettings::values.multiplayer_port = UISettings::values.multiplayer_port.GetDefault();
|
|
}
|
|
|
|
+ emit SaveConfig();
|
|
+
|
|
// attempt to connect in a different thread
|
|
QFuture<void> f = QtConcurrent::run([&] {
|
|
if (auto room_member = room_network.GetRoomMember().lock()) {
|
|
diff --git a/src/yuzu/multiplayer/direct_connect.h b/src/yuzu/multiplayer/direct_connect.h
|
|
index e39dd1e0d6df..b8f66cfb2b1f 100644
|
|
--- a/src/yuzu/multiplayer/direct_connect.h
|
|
+++ b/src/yuzu/multiplayer/direct_connect.h
|
|
@@ -31,6 +31,7 @@ class DirectConnectWindow : public QDialog {
|
|
* connections that it might have.
|
|
*/
|
|
void Closed();
|
|
+ void SaveConfig();
|
|
|
|
private slots:
|
|
void OnConnection();
|
|
diff --git a/src/yuzu/multiplayer/host_room.cpp b/src/yuzu/multiplayer/host_room.cpp
|
|
index 0c6adfd04007..a8faa5b248ba 100644
|
|
--- a/src/yuzu/multiplayer/host_room.cpp
|
|
+++ b/src/yuzu/multiplayer/host_room.cpp
|
|
@@ -232,6 +232,7 @@ void HostRoomWindow::Host() {
|
|
}
|
|
UISettings::values.multiplayer_room_description = ui->room_description->toPlainText();
|
|
ui->host->setEnabled(true);
|
|
+ emit SaveConfig();
|
|
close();
|
|
}
|
|
}
|
|
diff --git a/src/yuzu/multiplayer/host_room.h b/src/yuzu/multiplayer/host_room.h
|
|
index 034cb2eefd31..ae816e2e03c1 100644
|
|
--- a/src/yuzu/multiplayer/host_room.h
|
|
+++ b/src/yuzu/multiplayer/host_room.h
|
|
@@ -46,6 +46,9 @@ class HostRoomWindow : public QDialog {
|
|
void UpdateGameList(QStandardItemModel* list);
|
|
void RetranslateUi();
|
|
|
|
+signals:
|
|
+ void SaveConfig();
|
|
+
|
|
private:
|
|
void Host();
|
|
std::unique_ptr<Network::VerifyUser::Backend> CreateVerifyBackend(bool use_validation) const;
|
|
diff --git a/src/yuzu/multiplayer/lobby.cpp b/src/yuzu/multiplayer/lobby.cpp
|
|
index 107d40547679..08c275696463 100644
|
|
--- a/src/yuzu/multiplayer/lobby.cpp
|
|
+++ b/src/yuzu/multiplayer/lobby.cpp
|
|
@@ -7,6 +7,7 @@
|
|
#include "common/logging/log.h"
|
|
#include "common/settings.h"
|
|
#include "core/core.h"
|
|
+#include "core/hle/service/acc/profile_manager.h"
|
|
#include "core/internal_network/network_interface.h"
|
|
#include "network/network.h"
|
|
#include "ui_lobby.h"
|
|
@@ -26,9 +27,9 @@
|
|
Lobby::Lobby(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::Lobby>()),
|
|
- announce_multiplayer_session(session), system{system_}, room_network{
|
|
- system.GetRoomNetwork()} {
|
|
+ ui(std::make_unique<Ui::Lobby>()), announce_multiplayer_session(session),
|
|
+ profile_manager(std::make_unique<Service::Account::ProfileManager>()), system{system_},
|
|
+ room_network{system.GetRoomNetwork()} {
|
|
ui->setupUi(this);
|
|
|
|
// setup the watcher for background connections
|
|
@@ -60,9 +61,17 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list,
|
|
|
|
ui->nickname->setValidator(validation.GetNickname());
|
|
ui->nickname->setText(UISettings::values.multiplayer_nickname.GetValue());
|
|
- if (ui->nickname->text().isEmpty() && !Settings::values.yuzu_username.GetValue().empty()) {
|
|
- // Use yuzu Web Service user name as nickname by default
|
|
- ui->nickname->setText(QString::fromStdString(Settings::values.yuzu_username.GetValue()));
|
|
+
|
|
+ // Try find the best nickname by default
|
|
+ if (ui->nickname->text().isEmpty() || ui->nickname->text() == QStringLiteral("yuzu")) {
|
|
+ if (!Settings::values.yuzu_username.GetValue().empty()) {
|
|
+ ui->nickname->setText(
|
|
+ QString::fromStdString(Settings::values.yuzu_username.GetValue()));
|
|
+ } else if (!GetProfileUsername().empty()) {
|
|
+ ui->nickname->setText(QString::fromStdString(GetProfileUsername()));
|
|
+ } else {
|
|
+ ui->nickname->setText(QStringLiteral("yuzu"));
|
|
+ }
|
|
}
|
|
|
|
// UI Buttons
|
|
@@ -76,12 +85,6 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list,
|
|
// Actions
|
|
connect(&room_list_watcher, &QFutureWatcher<AnnounceMultiplayerRoom::RoomList>::finished, this,
|
|
&Lobby::OnRefreshLobby);
|
|
-
|
|
- // manually start a refresh when the window is opening
|
|
- // TODO(jroweboy): if this refresh is slow for people with bad internet, then don't do it as
|
|
- // part of the constructor, but offload the refresh until after the window shown. perhaps emit a
|
|
- // refreshroomlist signal from places that open the lobby
|
|
- RefreshLobby();
|
|
}
|
|
|
|
Lobby::~Lobby() = default;
|
|
@@ -96,6 +99,7 @@ void Lobby::UpdateGameList(QStandardItemModel* list) {
|
|
}
|
|
if (proxy)
|
|
proxy->UpdateGameList(game_list);
|
|
+ ui->room_list->sortByColumn(Column::GAME_NAME, Qt::AscendingOrder);
|
|
}
|
|
|
|
void Lobby::RetranslateUi() {
|
|
@@ -116,6 +120,11 @@ void Lobby::OnExpandRoom(const QModelIndex& index) {
|
|
}
|
|
|
|
void Lobby::OnJoinRoom(const QModelIndex& source) {
|
|
+ if (!Network::GetSelectedNetworkInterface()) {
|
|
+ LOG_INFO(WebService, "Automatically selected network interface for room network.");
|
|
+ Network::SelectFirstNetworkInterface();
|
|
+ }
|
|
+
|
|
if (!Network::GetSelectedNetworkInterface()) {
|
|
NetworkMessage::ErrorManager::ShowError(
|
|
NetworkMessage::ErrorManager::NO_INTERFACE_SELECTED);
|
|
@@ -197,16 +206,16 @@ void Lobby::OnJoinRoom(const QModelIndex& source) {
|
|
proxy->data(connection_index, LobbyItemHost::HostIPRole).toString();
|
|
UISettings::values.multiplayer_port =
|
|
proxy->data(connection_index, LobbyItemHost::HostPortRole).toInt();
|
|
+ emit SaveConfig();
|
|
}
|
|
|
|
void Lobby::ResetModel() {
|
|
model->clear();
|
|
model->insertColumns(0, Column::TOTAL);
|
|
- model->setHeaderData(Column::EXPAND, Qt::Horizontal, QString(), Qt::DisplayRole);
|
|
+ model->setHeaderData(Column::MEMBER, Qt::Horizontal, tr("Players"), Qt::DisplayRole);
|
|
model->setHeaderData(Column::ROOM_NAME, Qt::Horizontal, tr("Room Name"), Qt::DisplayRole);
|
|
model->setHeaderData(Column::GAME_NAME, Qt::Horizontal, tr("Preferred Game"), Qt::DisplayRole);
|
|
model->setHeaderData(Column::HOST, Qt::Horizontal, tr("Host"), Qt::DisplayRole);
|
|
- model->setHeaderData(Column::MEMBER, Qt::Horizontal, tr("Players"), Qt::DisplayRole);
|
|
}
|
|
|
|
void Lobby::RefreshLobby() {
|
|
@@ -229,6 +238,7 @@ void Lobby::OnRefreshLobby() {
|
|
for (int r = 0; r < game_list->rowCount(); ++r) {
|
|
auto index = game_list->index(r, 0);
|
|
auto game_id = game_list->data(index, GameListItemPath::ProgramIdRole).toULongLong();
|
|
+
|
|
if (game_id != 0 && room.information.preferred_game.id == game_id) {
|
|
smdh_icon = game_list->data(index, Qt::DecorationRole).value<QPixmap>();
|
|
}
|
|
@@ -243,17 +253,16 @@ void Lobby::OnRefreshLobby() {
|
|
members.append(var);
|
|
}
|
|
|
|
- auto first_item = new LobbyItem();
|
|
+ auto first_item = new LobbyItemGame(
|
|
+ room.information.preferred_game.id,
|
|
+ QString::fromStdString(room.information.preferred_game.name), smdh_icon);
|
|
auto row = QList<QStandardItem*>({
|
|
first_item,
|
|
new LobbyItemName(room.has_password, QString::fromStdString(room.information.name)),
|
|
- new LobbyItemGame(room.information.preferred_game.id,
|
|
- QString::fromStdString(room.information.preferred_game.name),
|
|
- smdh_icon),
|
|
+ new LobbyItemMemberList(members, room.information.member_slots),
|
|
new LobbyItemHost(QString::fromStdString(room.information.host_username),
|
|
QString::fromStdString(room.ip), room.information.port,
|
|
QString::fromStdString(room.verify_uid)),
|
|
- new LobbyItemMemberList(members, room.information.member_slots),
|
|
});
|
|
model->appendRow(row);
|
|
// To make the rows expandable, add the member data as a child of the first column of the
|
|
@@ -283,6 +292,26 @@ void Lobby::OnRefreshLobby() {
|
|
ui->room_list->setFirstColumnSpanned(j, proxy->index(i, 0), true);
|
|
}
|
|
}
|
|
+
|
|
+ ui->room_list->sortByColumn(Column::GAME_NAME, Qt::AscendingOrder);
|
|
+}
|
|
+
|
|
+std::string Lobby::GetProfileUsername() {
|
|
+ const auto& current_user = profile_manager->GetUser(Settings::values.current_user.GetValue());
|
|
+ Service::Account::ProfileBase profile{};
|
|
+
|
|
+ if (!current_user.has_value()) {
|
|
+ return "";
|
|
+ }
|
|
+
|
|
+ if (!profile_manager->GetProfileBase(*current_user, profile)) {
|
|
+ return "";
|
|
+ }
|
|
+
|
|
+ const auto text = Common::StringFromFixedZeroTerminatedBuffer(
|
|
+ reinterpret_cast<const char*>(profile.username.data()), profile.username.size());
|
|
+
|
|
+ return text;
|
|
}
|
|
|
|
LobbyFilterProxyModel::LobbyFilterProxyModel(QWidget* parent, QStandardItemModel* list)
|
|
diff --git a/src/yuzu/multiplayer/lobby.h b/src/yuzu/multiplayer/lobby.h
|
|
index 2696aec21a52..300dad13e11a 100644
|
|
--- a/src/yuzu/multiplayer/lobby.h
|
|
+++ b/src/yuzu/multiplayer/lobby.h
|
|
@@ -24,6 +24,10 @@ namespace Core {
|
|
class System;
|
|
}
|
|
|
|
+namespace Service::Account {
|
|
+class ProfileManager;
|
|
+}
|
|
+
|
|
/**
|
|
* Listing of all public games pulled from services. The lobby should be simple enough for users to
|
|
* find the game they want to play, and join it.
|
|
@@ -75,8 +79,11 @@ private slots:
|
|
|
|
signals:
|
|
void StateChanged(const Network::RoomMember::State&);
|
|
+ void SaveConfig();
|
|
|
|
private:
|
|
+ std::string GetProfileUsername();
|
|
+
|
|
/**
|
|
* Removes all entries in the Lobby before refreshing.
|
|
*/
|
|
@@ -96,6 +103,7 @@ private slots:
|
|
|
|
QFutureWatcher<AnnounceMultiplayerRoom::RoomList> room_list_watcher;
|
|
std::weak_ptr<Core::AnnounceMultiplayerSession> announce_multiplayer_session;
|
|
+ std::unique_ptr<Service::Account::ProfileManager> profile_manager;
|
|
QFutureWatcher<void>* watcher;
|
|
Validation validation;
|
|
Core::System& system;
|
|
diff --git a/src/yuzu/multiplayer/lobby_p.h b/src/yuzu/multiplayer/lobby_p.h
|
|
index 8071cede4d49..8b17075062b3 100644
|
|
--- a/src/yuzu/multiplayer/lobby_p.h
|
|
+++ b/src/yuzu/multiplayer/lobby_p.h
|
|
@@ -11,11 +11,10 @@
|
|
|
|
namespace Column {
|
|
enum List {
|
|
- EXPAND,
|
|
- ROOM_NAME,
|
|
GAME_NAME,
|
|
- HOST,
|
|
+ ROOM_NAME,
|
|
MEMBER,
|
|
+ HOST,
|
|
TOTAL,
|
|
};
|
|
}
|
|
@@ -98,7 +97,12 @@ class LobbyItemGame : public LobbyItem {
|
|
if (role == Qt::DecorationRole) {
|
|
auto val = data(GameIconRole);
|
|
if (val.isValid()) {
|
|
- val = val.value<QPixmap>().scaled(16, 16, Qt::KeepAspectRatio);
|
|
+ val = val.value<QPixmap>().scaled(32, 32, Qt::KeepAspectRatio,
|
|
+ Qt::TransformationMode::SmoothTransformation);
|
|
+ } else {
|
|
+ auto blank_image = QPixmap(32, 32);
|
|
+ blank_image.fill(Qt::black);
|
|
+ val = blank_image;
|
|
}
|
|
return val;
|
|
} else if (role != Qt::DisplayRole) {
|
|
@@ -191,8 +195,8 @@ class LobbyItemMemberList : public LobbyItem {
|
|
return LobbyItem::data(role);
|
|
}
|
|
auto members = data(MemberListRole).toList();
|
|
- return QStringLiteral("%1 / %2").arg(QString::number(members.size()),
|
|
- data(MaxPlayerRole).toString());
|
|
+ return QStringLiteral("%1 / %2 ")
|
|
+ .arg(QString::number(members.size()), data(MaxPlayerRole).toString());
|
|
}
|
|
|
|
bool operator<(const QStandardItem& other) const override {
|
|
diff --git a/src/yuzu/multiplayer/message.cpp b/src/yuzu/multiplayer/message.cpp
|
|
index 758b5b731d9a..6d8f18274f56 100644
|
|
--- a/src/yuzu/multiplayer/message.cpp
|
|
+++ b/src/yuzu/multiplayer/message.cpp
|
|
@@ -49,9 +49,9 @@ const ConnectionError ErrorManager::PERMISSION_DENIED(
|
|
QT_TR_NOOP("You do not have enough permission to perform this action."));
|
|
const ConnectionError ErrorManager::NO_SUCH_USER(QT_TR_NOOP(
|
|
"The user you are trying to kick/ban could not be found.\nThey may have left the room."));
|
|
-const ConnectionError ErrorManager::NO_INTERFACE_SELECTED(
|
|
- QT_TR_NOOP("No network interface is selected.\nPlease go to Configure -> System -> Network and "
|
|
- "make a selection."));
|
|
+const ConnectionError ErrorManager::NO_INTERFACE_SELECTED(QT_TR_NOOP(
|
|
+ "No valid network interface is selected.\nPlease go to Configure -> System -> Network and "
|
|
+ "make a selection."));
|
|
|
|
static bool WarnMessage(const std::string& title, const std::string& text) {
|
|
return QMessageBox::Ok == QMessageBox::warning(nullptr, QObject::tr(title.c_str()),
|
|
diff --git a/src/yuzu/multiplayer/state.cpp b/src/yuzu/multiplayer/state.cpp
|
|
index 3ad8460281db..ae2738ad4c17 100644
|
|
--- a/src/yuzu/multiplayer/state.cpp
|
|
+++ b/src/yuzu/multiplayer/state.cpp
|
|
@@ -44,9 +44,6 @@ MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_lis
|
|
|
|
status_text = new ClickableLabel(this);
|
|
status_icon = new ClickableLabel(this);
|
|
- status_text->setToolTip(tr("Current connection status"));
|
|
- status_text->setText(tr("Not Connected. Click here to find a room!"));
|
|
- status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16));
|
|
|
|
connect(status_text, &ClickableLabel::clicked, this, &MultiplayerState::OnOpenNetworkRoom);
|
|
connect(status_icon, &ClickableLabel::clicked, this, &MultiplayerState::OnOpenNetworkRoom);
|
|
@@ -57,6 +54,8 @@ MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_lis
|
|
HideNotification();
|
|
}
|
|
});
|
|
+
|
|
+ retranslateUi();
|
|
}
|
|
|
|
MultiplayerState::~MultiplayerState() = default;
|
|
@@ -90,14 +89,7 @@ void MultiplayerState::Close() {
|
|
void MultiplayerState::retranslateUi() {
|
|
status_text->setToolTip(tr("Current connection status"));
|
|
|
|
- if (current_state == Network::RoomMember::State::Uninitialized) {
|
|
- status_text->setText(tr("Not Connected. Click here to find a room!"));
|
|
- } else if (current_state == Network::RoomMember::State::Joined ||
|
|
- current_state == Network::RoomMember::State::Moderator) {
|
|
- status_text->setText(tr("Connected"));
|
|
- } else {
|
|
- status_text->setText(tr("Not Connected"));
|
|
- }
|
|
+ UpdateNotificationStatus();
|
|
|
|
if (lobby) {
|
|
lobby->RetranslateUi();
|
|
@@ -113,21 +105,55 @@ void MultiplayerState::retranslateUi() {
|
|
}
|
|
}
|
|
|
|
+void MultiplayerState::SetNotificationStatus(NotificationStatus status) {
|
|
+ notification_status = status;
|
|
+ UpdateNotificationStatus();
|
|
+}
|
|
+
|
|
+void MultiplayerState::UpdateNotificationStatus() {
|
|
+ switch (notification_status) {
|
|
+ case NotificationStatus::Unitialized:
|
|
+ status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16));
|
|
+ status_text->setText(tr("Not Connected. Click here to find a room!"));
|
|
+ leave_room->setEnabled(false);
|
|
+ show_room->setEnabled(false);
|
|
+ break;
|
|
+ case NotificationStatus::Disconnected:
|
|
+ status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16));
|
|
+ status_text->setText(tr("Not Connected"));
|
|
+ leave_room->setEnabled(false);
|
|
+ show_room->setEnabled(false);
|
|
+ break;
|
|
+ case NotificationStatus::Connected:
|
|
+ status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected")).pixmap(16));
|
|
+ status_text->setText(tr("Connected"));
|
|
+ leave_room->setEnabled(true);
|
|
+ show_room->setEnabled(true);
|
|
+ break;
|
|
+ case NotificationStatus::Notification:
|
|
+ status_icon->setPixmap(
|
|
+ QIcon::fromTheme(QStringLiteral("connected_notification")).pixmap(16));
|
|
+ status_text->setText(tr("New Messages Received"));
|
|
+ leave_room->setEnabled(true);
|
|
+ show_room->setEnabled(true);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ // Clean up status bar if game is running
|
|
+ if (system.IsPoweredOn()) {
|
|
+ status_text->clear();
|
|
+ }
|
|
+}
|
|
+
|
|
void MultiplayerState::OnNetworkStateChanged(const Network::RoomMember::State& state) {
|
|
LOG_DEBUG(Frontend, "Network State: {}", Network::GetStateStr(state));
|
|
if (state == Network::RoomMember::State::Joined ||
|
|
state == Network::RoomMember::State::Moderator) {
|
|
|
|
OnOpenNetworkRoom();
|
|
- status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected")).pixmap(16));
|
|
- status_text->setText(tr("Connected"));
|
|
- leave_room->setEnabled(true);
|
|
- show_room->setEnabled(true);
|
|
+ SetNotificationStatus(NotificationStatus::Connected);
|
|
} else {
|
|
- status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16));
|
|
- status_text->setText(tr("Not Connected"));
|
|
- leave_room->setEnabled(false);
|
|
- show_room->setEnabled(false);
|
|
+ SetNotificationStatus(NotificationStatus::Disconnected);
|
|
}
|
|
|
|
current_state = state;
|
|
@@ -185,6 +211,10 @@ void MultiplayerState::OnAnnounceFailed(const WebService::WebResult& result) {
|
|
QMessageBox::Ok);
|
|
}
|
|
|
|
+void MultiplayerState::OnSaveConfig() {
|
|
+ emit SaveConfig();
|
|
+}
|
|
+
|
|
void MultiplayerState::UpdateThemedIcons() {
|
|
if (show_notification) {
|
|
status_icon->setPixmap(
|
|
@@ -209,13 +239,16 @@ static void BringWidgetToFront(QWidget* widget) {
|
|
void MultiplayerState::OnViewLobby() {
|
|
if (lobby == nullptr) {
|
|
lobby = new Lobby(this, game_list_model, announce_multiplayer_session, system);
|
|
+ connect(lobby, &Lobby::SaveConfig, this, &MultiplayerState::OnSaveConfig);
|
|
}
|
|
+ lobby->RefreshLobby();
|
|
BringWidgetToFront(lobby);
|
|
}
|
|
|
|
void MultiplayerState::OnCreateRoom() {
|
|
if (host_room == nullptr) {
|
|
host_room = new HostRoomWindow(this, game_list_model, announce_multiplayer_session, system);
|
|
+ connect(host_room, &HostRoomWindow::SaveConfig, this, &MultiplayerState::OnSaveConfig);
|
|
}
|
|
BringWidgetToFront(host_room);
|
|
}
|
|
@@ -250,14 +283,12 @@ void MultiplayerState::ShowNotification() {
|
|
show_notification = true;
|
|
QApplication::alert(nullptr);
|
|
QApplication::beep();
|
|
- status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected_notification")).pixmap(16));
|
|
- status_text->setText(tr("New Messages Received"));
|
|
+ SetNotificationStatus(NotificationStatus::Notification);
|
|
}
|
|
|
|
void MultiplayerState::HideNotification() {
|
|
show_notification = false;
|
|
- status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected")).pixmap(16));
|
|
- status_text->setText(tr("Connected"));
|
|
+ SetNotificationStatus(NotificationStatus::Connected);
|
|
}
|
|
|
|
void MultiplayerState::OnOpenNetworkRoom() {
|
|
@@ -280,6 +311,8 @@ void MultiplayerState::OnOpenNetworkRoom() {
|
|
void MultiplayerState::OnDirectConnectToRoom() {
|
|
if (direct_connect == nullptr) {
|
|
direct_connect = new DirectConnectWindow(system, this);
|
|
+ connect(direct_connect, &DirectConnectWindow::SaveConfig, this,
|
|
+ &MultiplayerState::OnSaveConfig);
|
|
}
|
|
BringWidgetToFront(direct_connect);
|
|
}
|
|
diff --git a/src/yuzu/multiplayer/state.h b/src/yuzu/multiplayer/state.h
|
|
index c92496413ca5..5d681c5c6131 100644
|
|
--- a/src/yuzu/multiplayer/state.h
|
|
+++ b/src/yuzu/multiplayer/state.h
|
|
@@ -22,6 +22,13 @@ class MultiplayerState : public QWidget {
|
|
Q_OBJECT;
|
|
|
|
public:
|
|
+ enum class NotificationStatus {
|
|
+ Unitialized,
|
|
+ Disconnected,
|
|
+ Connected,
|
|
+ Notification,
|
|
+ };
|
|
+
|
|
explicit MultiplayerState(QWidget* parent, QStandardItemModel* game_list, QAction* leave_room,
|
|
QAction* show_room, Core::System& system_);
|
|
~MultiplayerState();
|
|
@@ -31,6 +38,10 @@ class MultiplayerState : public QWidget {
|
|
*/
|
|
void Close();
|
|
|
|
+ void SetNotificationStatus(NotificationStatus state);
|
|
+
|
|
+ void UpdateNotificationStatus();
|
|
+
|
|
ClickableLabel* GetStatusText() const {
|
|
return status_text;
|
|
}
|
|
@@ -64,6 +75,7 @@ public slots:
|
|
void OnOpenNetworkRoom();
|
|
void OnDirectConnectToRoom();
|
|
void OnAnnounceFailed(const WebService::WebResult&);
|
|
+ void OnSaveConfig();
|
|
void UpdateThemedIcons();
|
|
void ShowNotification();
|
|
void HideNotification();
|
|
@@ -72,6 +84,7 @@ public slots:
|
|
void NetworkStateChanged(const Network::RoomMember::State&);
|
|
void NetworkError(const Network::RoomMember::Error&);
|
|
void AnnounceFailed(const WebService::WebResult&);
|
|
+ void SaveConfig();
|
|
|
|
private:
|
|
Lobby* lobby = nullptr;
|
|
@@ -85,6 +98,7 @@ public slots:
|
|
QAction* show_room;
|
|
std::shared_ptr<Core::AnnounceMultiplayerSession> announce_multiplayer_session;
|
|
Network::RoomMember::State current_state = Network::RoomMember::State::Uninitialized;
|
|
+ NotificationStatus notification_status = NotificationStatus::Unitialized;
|
|
bool has_mod_perms = false;
|
|
Network::RoomMember::CallbackHandle<Network::RoomMember::State> state_callback_handle;
|
|
Network::RoomMember::CallbackHandle<Network::RoomMember::Error> error_callback_handle;
|
|
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
|
|
index e12d414d96b7..753797efc4d9 100644
|
|
--- a/src/yuzu/uisettings.h
|
|
+++ b/src/yuzu/uisettings.h
|
|
@@ -102,7 +102,7 @@ struct Values {
|
|
Settings::Setting<uint32_t> callout_flags{0, "calloutFlags"};
|
|
|
|
// multiplayer settings
|
|
- Settings::Setting<QString> multiplayer_nickname{QStringLiteral("yuzu"), "nickname"};
|
|
+ Settings::Setting<QString> multiplayer_nickname{{}, "nickname"};
|
|
Settings::Setting<QString> multiplayer_ip{{}, "ip"};
|
|
Settings::SwitchableSetting<uint, true> multiplayer_port{24872, 0, UINT16_MAX, "port"};
|
|
Settings::Setting<QString> multiplayer_room_nickname{{}, "room_nickname"};
|
|
|
|
From 1694c55d62d44730d760371d1bf2559de1b191dd Mon Sep 17 00:00:00 2001
|
|
From: Narr the Reg <juangerman-13@hotmail.com>
|
|
Date: Sat, 10 Sep 2022 12:57:19 -0500
|
|
Subject: [PATCH 3/5] fix black icon
|
|
|
|
---
|
|
src/yuzu/multiplayer/lobby_p.h | 2 ++
|
|
1 file changed, 2 insertions(+)
|
|
|
|
diff --git a/src/yuzu/multiplayer/lobby_p.h b/src/yuzu/multiplayer/lobby_p.h
|
|
index 8b17075062b3..068c95acad75 100644
|
|
--- a/src/yuzu/multiplayer/lobby_p.h
|
|
+++ b/src/yuzu/multiplayer/lobby_p.h
|
|
@@ -90,6 +90,8 @@ class LobbyItemGame : public LobbyItem {
|
|
setData(game_name, GameNameRole);
|
|
if (!smdh_icon.isNull()) {
|
|
setData(smdh_icon, GameIconRole);
|
|
+ } else {
|
|
+ setData(QIcon::fromTheme(QStringLiteral("chip")).pixmap(32), GameIconRole);
|
|
}
|
|
}
|
|
|
|
|
|
From aa11d73bba386076973010ba4c60d5b04ba828a3 Mon Sep 17 00:00:00 2001
|
|
From: liushuyu <liushuyu011@gmail.com>
|
|
Date: Sat, 10 Sep 2022 17:38:36 -0600
|
|
Subject: [PATCH 4/5] dedicated_room: fix token padding ...
|
|
|
|
... mebedtls' base64 routine has a strange behavioral issue where if the
|
|
input is invalid, it will not report it as invalid, but rather returning
|
|
a bunch of garbage data. This new round-tripping padding method should
|
|
eliminate such issue.
|
|
---
|
|
src/dedicated_room/yuzu_room.cpp | 14 ++++++++++++--
|
|
1 file changed, 12 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/src/dedicated_room/yuzu_room.cpp b/src/dedicated_room/yuzu_room.cpp
|
|
index 8d8ac1ed74f1..359891883c4a 100644
|
|
--- a/src/dedicated_room/yuzu_room.cpp
|
|
+++ b/src/dedicated_room/yuzu_room.cpp
|
|
@@ -76,8 +76,18 @@ static constexpr char BanListMagic[] = "YuzuRoom-BanList-1";
|
|
static constexpr char token_delimiter{':'};
|
|
|
|
static void PadToken(std::string& token) {
|
|
- const auto remainder = token.size() % 3;
|
|
- for (size_t i = 0; i < (3 - remainder); i++) {
|
|
+ std::size_t outlen = 0;
|
|
+
|
|
+ std::array<unsigned char, 512> output{};
|
|
+ std::array<unsigned char, 2048> roundtrip{};
|
|
+ for (size_t i = 0; i < 3; i++) {
|
|
+ mbedtls_base64_decode(output.data(), output.size(), &outlen,
|
|
+ reinterpret_cast<const unsigned char*>(token.c_str()),
|
|
+ token.length());
|
|
+ mbedtls_base64_encode(roundtrip.data(), roundtrip.size(), &outlen, output.data(), outlen);
|
|
+ if (memcmp(roundtrip.data(), token.data(), token.size()) == 0) {
|
|
+ break;
|
|
+ }
|
|
token.push_back('=');
|
|
}
|
|
}
|
|
|
|
From 6b9e6953e8bb91df1ae7c1a6a6076f57ad628c44 Mon Sep 17 00:00:00 2001
|
|
From: FearlessTobi <thm.frey@gmail.com>
|
|
Date: Mon, 12 Sep 2022 22:39:18 +0200
|
|
Subject: [PATCH 5/5] Address some review comments
|
|
|
|
---
|
|
src/core/hle/service/ldn/lan_discovery.cpp | 41 +++++++------------
|
|
src/core/hle/service/ldn/lan_discovery.h | 13 +++---
|
|
src/core/hle/service/ldn/ldn.cpp | 2 -
|
|
src/core/hle/service/ldn/ldn_types.h | 8 +---
|
|
.../internal_network/network_interface.cpp | 4 +-
|
|
src/network/room_member.cpp | 2 +-
|
|
6 files changed, 26 insertions(+), 44 deletions(-)
|
|
|
|
diff --git a/src/core/hle/service/ldn/lan_discovery.cpp b/src/core/hle/service/ldn/lan_discovery.cpp
|
|
index b04c990771ad..5f3222217f00 100644
|
|
--- a/src/core/hle/service/ldn/lan_discovery.cpp
|
|
+++ b/src/core/hle/service/ldn/lan_discovery.cpp
|
|
@@ -35,12 +35,9 @@ void LanStation::OverrideInfo() {
|
|
|
|
LANDiscovery::LANDiscovery(Network::RoomNetwork& room_network_)
|
|
: stations({{{1, this}, {2, this}, {3, this}, {4, this}, {5, this}, {6, this}, {7, this}}}),
|
|
- room_network{room_network_} {
|
|
- LOG_INFO(Service_LDN, "LANDiscovery");
|
|
-}
|
|
+ room_network{room_network_} {}
|
|
|
|
LANDiscovery::~LANDiscovery() {
|
|
- LOG_INFO(Service_LDN, "~LANDiscovery");
|
|
if (inited) {
|
|
Result rc = Finalize();
|
|
LOG_INFO(Service_LDN, "Finalize: {}", rc.raw);
|
|
@@ -62,7 +59,7 @@ void LANDiscovery::InitNetworkInfo() {
|
|
}
|
|
|
|
void LANDiscovery::InitNodeStateChange() {
|
|
- for (auto& node_update : nodeChanges) {
|
|
+ for (auto& node_update : node_changes) {
|
|
node_update.state_change = NodeStateChange::None;
|
|
}
|
|
for (auto& node_state : node_last_states) {
|
|
@@ -97,8 +94,8 @@ Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network,
|
|
if (state == State::AccessPointCreated || state == State::StationConnected) {
|
|
std::memcpy(&out_network, &network_info, sizeof(network_info));
|
|
for (std::size_t i = 0; i < buffer_count; i++) {
|
|
- out_updates[i].state_change = nodeChanges[i].state_change;
|
|
- nodeChanges[i].state_change = NodeStateChange::None;
|
|
+ out_updates[i].state_change = node_changes[i].state_change;
|
|
+ node_changes[i].state_change = NodeStateChange::None;
|
|
}
|
|
return ResultSuccess;
|
|
}
|
|
@@ -168,9 +165,9 @@ Result LANDiscovery::Scan(std::vector<NetworkInfo>& networks, u16& count,
|
|
return ResultSuccess;
|
|
}
|
|
|
|
-Result LANDiscovery::SetAdvertiseData(std::vector<u8>& data) {
|
|
+Result LANDiscovery::SetAdvertiseData(std::span<const u8> data) {
|
|
std::scoped_lock lock{packet_mutex};
|
|
- std::size_t size = data.size();
|
|
+ const std::size_t size = data.size();
|
|
if (size > AdvertiseDataSizeMax) {
|
|
return ResultAdvertiseDataTooLarge;
|
|
}
|
|
@@ -328,7 +325,7 @@ Result LANDiscovery::Disconnect() {
|
|
return ResultSuccess;
|
|
}
|
|
|
|
-Result LANDiscovery::Initialize(LanEventFunc lan_event, bool listening) {
|
|
+Result LANDiscovery::Initialize(LanEventFunc lan_event_, bool listening) {
|
|
std::scoped_lock lock{packet_mutex};
|
|
if (inited) {
|
|
return ResultSuccess;
|
|
@@ -341,7 +338,7 @@ Result LANDiscovery::Initialize(LanEventFunc lan_event, bool listening) {
|
|
}
|
|
|
|
connected_clients.clear();
|
|
- LanEvent = lan_event;
|
|
+ LanEvent = lan_event_;
|
|
|
|
SetState(State::Initialized);
|
|
|
|
@@ -439,7 +436,6 @@ void LANDiscovery::SendPacket(Network::LDNPacketType type, const Data& data,
|
|
packet.local_ip = GetLocalIp();
|
|
packet.remote_ip = remote_ip;
|
|
|
|
- packet.data.clear();
|
|
packet.data.resize(sizeof(data));
|
|
std::memcpy(packet.data.data(), &data, sizeof(data));
|
|
SendPacket(packet);
|
|
@@ -453,7 +449,6 @@ void LANDiscovery::SendPacket(Network::LDNPacketType type, Ipv4Address remote_ip
|
|
packet.local_ip = GetLocalIp();
|
|
packet.remote_ip = remote_ip;
|
|
|
|
- packet.data.clear();
|
|
SendPacket(packet);
|
|
}
|
|
|
|
@@ -465,7 +460,6 @@ void LANDiscovery::SendBroadcast(Network::LDNPacketType type, const Data& data)
|
|
packet.broadcast = true;
|
|
packet.local_ip = GetLocalIp();
|
|
|
|
- packet.data.clear();
|
|
packet.data.resize(sizeof(data));
|
|
std::memcpy(packet.data.data(), &data, sizeof(data));
|
|
SendPacket(packet);
|
|
@@ -478,7 +472,6 @@ void LANDiscovery::SendBroadcast(Network::LDNPacketType type) {
|
|
packet.broadcast = true;
|
|
packet.local_ip = GetLocalIp();
|
|
|
|
- packet.data.clear();
|
|
SendPacket(packet);
|
|
}
|
|
|
|
@@ -581,9 +574,9 @@ bool LANDiscovery::IsNodeStateChanged() {
|
|
for (int i = 0; i < NodeCountMax; i++) {
|
|
if (nodes[i].is_connected != node_last_states[i]) {
|
|
if (nodes[i].is_connected) {
|
|
- nodeChanges[i].state_change |= NodeStateChange::Connect;
|
|
+ node_changes[i].state_change |= NodeStateChange::Connect;
|
|
} else {
|
|
- nodeChanges[i].state_change |= NodeStateChange::Disconnect;
|
|
+ node_changes[i].state_change |= NodeStateChange::Disconnect;
|
|
}
|
|
node_last_states[i] = nodes[i].is_connected;
|
|
changed = true;
|
|
@@ -598,15 +591,11 @@ bool LANDiscovery::IsFlagSet(ScanFilterFlag flag, ScanFilterFlag search_flag) co
|
|
return (flag_value & search_flag_value) == search_flag_value;
|
|
}
|
|
|
|
-int LANDiscovery::GetStationCount() {
|
|
- int count = 0;
|
|
- for (const auto& station : stations) {
|
|
- if (station.GetStatus() != NodeStatus::Disconnected) {
|
|
- count++;
|
|
- }
|
|
- }
|
|
-
|
|
- return count;
|
|
+int LANDiscovery::GetStationCount() const {
|
|
+ return static_cast<int>(
|
|
+ std::count_if(stations.begin(), stations.end(), [](const auto& station) {
|
|
+ return station.GetStatus() != NodeStatus::Disconnected;
|
|
+ }));
|
|
}
|
|
|
|
MacAddress LANDiscovery::GetFakeMac() const {
|
|
diff --git a/src/core/hle/service/ldn/lan_discovery.h b/src/core/hle/service/ldn/lan_discovery.h
|
|
index 255342456eba..e64e89424542 100644
|
|
--- a/src/core/hle/service/ldn/lan_discovery.h
|
|
+++ b/src/core/hle/service/ldn/lan_discovery.h
|
|
@@ -10,6 +10,7 @@
|
|
#include <mutex>
|
|
#include <optional>
|
|
#include <random>
|
|
+#include <span>
|
|
#include <thread>
|
|
#include <unordered_map>
|
|
|
|
@@ -44,7 +45,7 @@ class LanStation {
|
|
|
|
class LANDiscovery {
|
|
public:
|
|
- typedef std::function<void()> LanEventFunc;
|
|
+ using LanEventFunc = std::function<void()>;
|
|
|
|
LANDiscovery(Network::RoomNetwork& room_network_);
|
|
~LANDiscovery();
|
|
@@ -58,7 +59,7 @@ class LANDiscovery {
|
|
|
|
DisconnectReason GetDisconnectReason() const;
|
|
Result Scan(std::vector<NetworkInfo>& networks, u16& count, const ScanFilter& filter);
|
|
- Result SetAdvertiseData(std::vector<u8>& data);
|
|
+ Result SetAdvertiseData(std::span<const u8> data);
|
|
|
|
Result OpenAccessPoint();
|
|
Result CloseAccessPoint();
|
|
@@ -74,7 +75,7 @@ class LANDiscovery {
|
|
u16 local_communication_version);
|
|
Result Disconnect();
|
|
|
|
- Result Initialize(LanEventFunc lan_event = empty_func, bool listening = true);
|
|
+ Result Initialize(LanEventFunc lan_event_ = empty_func, bool listening = true);
|
|
Result Finalize();
|
|
|
|
void ReceivePacket(const Network::LDNPacket& packet);
|
|
@@ -94,7 +95,7 @@ class LANDiscovery {
|
|
|
|
bool IsNodeStateChanged();
|
|
bool IsFlagSet(ScanFilterFlag flag, ScanFilterFlag search_flag) const;
|
|
- int GetStationCount();
|
|
+ int GetStationCount() const;
|
|
MacAddress GetFakeMac() const;
|
|
Result GetNodeInfo(NodeInfo& node, const UserConfig& user_config,
|
|
u16 local_communication_version);
|
|
@@ -114,7 +115,7 @@ class LANDiscovery {
|
|
bool inited{};
|
|
std::mutex packet_mutex;
|
|
std::array<LanStation, StationCountMax> stations;
|
|
- std::array<NodeLatestUpdate, NodeCountMax> nodeChanges{};
|
|
+ std::array<NodeLatestUpdate, NodeCountMax> node_changes{};
|
|
std::array<u8, NodeCountMax> node_last_states{};
|
|
std::unordered_map<MacAddress, NetworkInfo, MACAddressHash> scan_results{};
|
|
NodeInfo node_info{};
|
|
@@ -124,7 +125,7 @@ class LANDiscovery {
|
|
|
|
// TODO (flTobi): Should this be an std::set?
|
|
std::vector<Ipv4Address> connected_clients;
|
|
- std::optional<Ipv4Address> host_ip = std::nullopt;
|
|
+ std::optional<Ipv4Address> host_ip;
|
|
|
|
LanEventFunc LanEvent;
|
|
|
|
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp
|
|
index 6537f49cf2e9..ea3e7e55af62 100644
|
|
--- a/src/core/hle/service/ldn/ldn.cpp
|
|
+++ b/src/core/hle/service/ldn/ldn.cpp
|
|
@@ -205,8 +205,6 @@ class IUserLocalCommunicationService final
|
|
}
|
|
|
|
void GetIpv4Address(Kernel::HLERequestContext& ctx) {
|
|
- LOG_CRITICAL(Service_LDN, "called");
|
|
-
|
|
const auto network_interface = Network::GetSelectedNetworkInterface();
|
|
|
|
if (!network_interface) {
|
|
diff --git a/src/core/hle/service/ldn/ldn_types.h b/src/core/hle/service/ldn/ldn_types.h
|
|
index d6609fff55fd..de3cd6b762fd 100644
|
|
--- a/src/core/hle/service/ldn/ldn_types.h
|
|
+++ b/src/core/hle/service/ldn/ldn_types.h
|
|
@@ -31,13 +31,7 @@ enum class NodeStateChange : u8 {
|
|
DisconnectAndConnect,
|
|
};
|
|
|
|
-inline NodeStateChange operator|(NodeStateChange a, NodeStateChange b) {
|
|
- return static_cast<NodeStateChange>(static_cast<u8>(a) | static_cast<u8>(b));
|
|
-}
|
|
-
|
|
-inline NodeStateChange operator|=(NodeStateChange& a, NodeStateChange b) {
|
|
- return a = a | b;
|
|
-}
|
|
+DECLARE_ENUM_FLAG_OPERATORS(NodeStateChange)
|
|
|
|
enum class ScanFilterFlag : u32 {
|
|
None = 0,
|
|
diff --git a/src/core/internal_network/network_interface.cpp b/src/core/internal_network/network_interface.cpp
|
|
index 858ae1cfbbb2..057fd3661748 100644
|
|
--- a/src/core/internal_network/network_interface.cpp
|
|
+++ b/src/core/internal_network/network_interface.cpp
|
|
@@ -188,7 +188,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
|
|
std::optional<NetworkInterface> GetSelectedNetworkInterface() {
|
|
const auto& selected_network_interface = Settings::values.network_interface.GetValue();
|
|
const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
|
|
- if (network_interfaces.size() == 0) {
|
|
+ if (network_interfaces.empty()) {
|
|
LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces");
|
|
return std::nullopt;
|
|
}
|
|
@@ -209,7 +209,7 @@ std::optional<NetworkInterface> GetSelectedNetworkInterface() {
|
|
void SelectFirstNetworkInterface() {
|
|
const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
|
|
|
|
- if (network_interfaces.size() == 0) {
|
|
+ if (network_interfaces.empty()) {
|
|
return;
|
|
}
|
|
|
|
diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp
|
|
index 572e55a5b83c..b94cb24ad74c 100644
|
|
--- a/src/network/room_member.cpp
|
|
+++ b/src/network/room_member.cpp
|
|
@@ -716,7 +716,7 @@ RoomMember::CallbackHandle<ProxyPacket> RoomMember::BindOnProxyPacketReceived(
|
|
|
|
RoomMember::CallbackHandle<LDNPacket> RoomMember::BindOnLdnPacketReceived(
|
|
std::function<void(const LDNPacket&)> callback) {
|
|
- return room_member_impl->Bind(callback);
|
|
+ return room_member_impl->Bind(std::move(callback));
|
|
}
|
|
|
|
RoomMember::CallbackHandle<RoomInformation> RoomMember::BindOnRoomInformationChanged(
|