diff options
Diffstat (limited to 'src')
41 files changed, 1439 insertions, 218 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7fd2d0276..8e3fd4505 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -496,6 +496,8 @@ add_library(core STATIC | |||
| 496 | hle/service/jit/jit.h | 496 | hle/service/jit/jit.h |
| 497 | hle/service/lbl/lbl.cpp | 497 | hle/service/lbl/lbl.cpp |
| 498 | hle/service/lbl/lbl.h | 498 | hle/service/lbl/lbl.h |
| 499 | hle/service/ldn/lan_discovery.cpp | ||
| 500 | hle/service/ldn/lan_discovery.h | ||
| 499 | hle/service/ldn/ldn_results.h | 501 | hle/service/ldn/ldn_results.h |
| 500 | hle/service/ldn/ldn.cpp | 502 | hle/service/ldn/ldn.cpp |
| 501 | hle/service/ldn/ldn.h | 503 | hle/service/ldn/ldn.h |
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 142c39003..e27d84734 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp | |||
| @@ -93,7 +93,7 @@ void EmulatedController::ReloadFromSettings() { | |||
| 93 | .body = GetNpadColor(player.body_color_left), | 93 | .body = GetNpadColor(player.body_color_left), |
| 94 | .button = GetNpadColor(player.button_color_left), | 94 | .button = GetNpadColor(player.button_color_left), |
| 95 | }; | 95 | }; |
| 96 | controller.colors_state.left = { | 96 | controller.colors_state.right = { |
| 97 | .body = GetNpadColor(player.body_color_right), | 97 | .body = GetNpadColor(player.body_color_right), |
| 98 | .button = GetNpadColor(player.button_color_right), | 98 | .button = GetNpadColor(player.button_color_right), |
| 99 | }; | 99 | }; |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index de3fae2cb..46bad7871 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -36,7 +36,8 @@ namespace Service::HID { | |||
| 36 | 36 | ||
| 37 | // Updating period for each HID device. | 37 | // Updating period for each HID device. |
| 38 | // Period time is obtained by measuring the number of samples in a second on HW using a homebrew | 38 | // Period time is obtained by measuring the number of samples in a second on HW using a homebrew |
| 39 | constexpr auto pad_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 250Hz) | 39 | // Correct pad_update_ns is 4ms this is overclocked to lower input lag |
| 40 | constexpr auto pad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz) | ||
| 40 | constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) | 41 | constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) |
| 41 | constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz) | 42 | constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz) |
| 42 | 43 | ||
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 000000000..8f3c04550 --- /dev/null +++ b/src/core/hle/service/ldn/lan_discovery.cpp | |||
| @@ -0,0 +1,633 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/service/ldn/lan_discovery.h" | ||
| 5 | #include "core/internal_network/network.h" | ||
| 6 | #include "core/internal_network/network_interface.h" | ||
| 7 | |||
| 8 | namespace Service::LDN { | ||
| 9 | |||
| 10 | LanStation::LanStation(s8 node_id_, LANDiscovery* discovery_) | ||
| 11 | : node_info(nullptr), status(NodeStatus::Disconnected), node_id(node_id_), | ||
| 12 | discovery(discovery_) {} | ||
| 13 | |||
| 14 | LanStation::~LanStation() = default; | ||
| 15 | |||
| 16 | NodeStatus LanStation::GetStatus() const { | ||
| 17 | return status; | ||
| 18 | } | ||
| 19 | |||
| 20 | void LanStation::OnClose() { | ||
| 21 | LOG_INFO(Service_LDN, "OnClose {}", node_id); | ||
| 22 | Reset(); | ||
| 23 | discovery->UpdateNodes(); | ||
| 24 | } | ||
| 25 | |||
| 26 | void LanStation::Reset() { | ||
| 27 | status = NodeStatus::Disconnected; | ||
| 28 | }; | ||
| 29 | |||
| 30 | void LanStation::OverrideInfo() { | ||
| 31 | bool connected = GetStatus() == NodeStatus::Connected; | ||
| 32 | node_info->node_id = node_id; | ||
| 33 | node_info->is_connected = connected ? 1 : 0; | ||
| 34 | } | ||
| 35 | |||
| 36 | LANDiscovery::LANDiscovery(Network::RoomNetwork& room_network_) | ||
| 37 | : stations({{{1, this}, {2, this}, {3, this}, {4, this}, {5, this}, {6, this}, {7, this}}}), | ||
| 38 | room_network{room_network_} {} | ||
| 39 | |||
| 40 | LANDiscovery::~LANDiscovery() { | ||
| 41 | if (inited) { | ||
| 42 | Result rc = Finalize(); | ||
| 43 | LOG_INFO(Service_LDN, "Finalize: {}", rc.raw); | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | void LANDiscovery::InitNetworkInfo() { | ||
| 48 | network_info.common.bssid = GetFakeMac(); | ||
| 49 | network_info.common.channel = WifiChannel::Wifi24_6; | ||
| 50 | network_info.common.link_level = LinkLevel::Good; | ||
| 51 | network_info.common.network_type = PackedNetworkType::Ldn; | ||
| 52 | network_info.common.ssid = fake_ssid; | ||
| 53 | |||
| 54 | auto& nodes = network_info.ldn.nodes; | ||
| 55 | for (std::size_t i = 0; i < NodeCountMax; i++) { | ||
| 56 | nodes[i].node_id = static_cast<s8>(i); | ||
| 57 | nodes[i].is_connected = 0; | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | void LANDiscovery::InitNodeStateChange() { | ||
| 62 | for (auto& node_update : node_changes) { | ||
| 63 | node_update.state_change = NodeStateChange::None; | ||
| 64 | } | ||
| 65 | for (auto& node_state : node_last_states) { | ||
| 66 | node_state = 0; | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | State LANDiscovery::GetState() const { | ||
| 71 | return state; | ||
| 72 | } | ||
| 73 | |||
| 74 | void LANDiscovery::SetState(State new_state) { | ||
| 75 | state = new_state; | ||
| 76 | } | ||
| 77 | |||
| 78 | Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network) const { | ||
| 79 | if (state == State::AccessPointCreated || state == State::StationConnected) { | ||
| 80 | std::memcpy(&out_network, &network_info, sizeof(network_info)); | ||
| 81 | return ResultSuccess; | ||
| 82 | } | ||
| 83 | |||
| 84 | return ResultBadState; | ||
| 85 | } | ||
| 86 | |||
| 87 | Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network, | ||
| 88 | std::vector<NodeLatestUpdate>& out_updates, | ||
| 89 | std::size_t buffer_count) { | ||
| 90 | if (buffer_count > NodeCountMax) { | ||
| 91 | return ResultInvalidBufferCount; | ||
| 92 | } | ||
| 93 | |||
| 94 | if (state == State::AccessPointCreated || state == State::StationConnected) { | ||
| 95 | std::memcpy(&out_network, &network_info, sizeof(network_info)); | ||
| 96 | for (std::size_t i = 0; i < buffer_count; i++) { | ||
| 97 | out_updates[i].state_change = node_changes[i].state_change; | ||
| 98 | node_changes[i].state_change = NodeStateChange::None; | ||
| 99 | } | ||
| 100 | return ResultSuccess; | ||
| 101 | } | ||
| 102 | |||
| 103 | return ResultBadState; | ||
| 104 | } | ||
| 105 | |||
| 106 | DisconnectReason LANDiscovery::GetDisconnectReason() const { | ||
| 107 | return disconnect_reason; | ||
| 108 | } | ||
| 109 | |||
| 110 | Result LANDiscovery::Scan(std::vector<NetworkInfo>& networks, u16& count, | ||
| 111 | const ScanFilter& filter) { | ||
| 112 | if (!IsFlagSet(filter.flag, ScanFilterFlag::NetworkType) || | ||
| 113 | filter.network_type <= NetworkType::All) { | ||
| 114 | if (!IsFlagSet(filter.flag, ScanFilterFlag::Ssid) && filter.ssid.length >= SsidLengthMax) { | ||
| 115 | return ResultBadInput; | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | { | ||
| 120 | std::scoped_lock lock{packet_mutex}; | ||
| 121 | scan_results.clear(); | ||
| 122 | |||
| 123 | SendBroadcast(Network::LDNPacketType::Scan); | ||
| 124 | } | ||
| 125 | |||
| 126 | LOG_INFO(Service_LDN, "Waiting for scan replies"); | ||
| 127 | std::this_thread::sleep_for(std::chrono::seconds(1)); | ||
| 128 | |||
| 129 | std::scoped_lock lock{packet_mutex}; | ||
| 130 | for (const auto& [key, info] : scan_results) { | ||
| 131 | if (count >= networks.size()) { | ||
| 132 | break; | ||
| 133 | } | ||
| 134 | |||
| 135 | if (IsFlagSet(filter.flag, ScanFilterFlag::LocalCommunicationId)) { | ||
| 136 | if (filter.network_id.intent_id.local_communication_id != | ||
| 137 | info.network_id.intent_id.local_communication_id) { | ||
| 138 | continue; | ||
| 139 | } | ||
| 140 | } | ||
| 141 | if (IsFlagSet(filter.flag, ScanFilterFlag::SessionId)) { | ||
| 142 | if (filter.network_id.session_id != info.network_id.session_id) { | ||
| 143 | continue; | ||
| 144 | } | ||
| 145 | } | ||
| 146 | if (IsFlagSet(filter.flag, ScanFilterFlag::NetworkType)) { | ||
| 147 | if (filter.network_type != static_cast<NetworkType>(info.common.network_type)) { | ||
| 148 | continue; | ||
| 149 | } | ||
| 150 | } | ||
| 151 | if (IsFlagSet(filter.flag, ScanFilterFlag::Ssid)) { | ||
| 152 | if (filter.ssid != info.common.ssid) { | ||
| 153 | continue; | ||
| 154 | } | ||
| 155 | } | ||
| 156 | if (IsFlagSet(filter.flag, ScanFilterFlag::SceneId)) { | ||
| 157 | if (filter.network_id.intent_id.scene_id != info.network_id.intent_id.scene_id) { | ||
| 158 | continue; | ||
| 159 | } | ||
| 160 | } | ||
| 161 | |||
| 162 | networks[count++] = info; | ||
| 163 | } | ||
| 164 | |||
| 165 | return ResultSuccess; | ||
| 166 | } | ||
| 167 | |||
| 168 | Result LANDiscovery::SetAdvertiseData(std::span<const u8> data) { | ||
| 169 | std::scoped_lock lock{packet_mutex}; | ||
| 170 | const std::size_t size = data.size(); | ||
| 171 | if (size > AdvertiseDataSizeMax) { | ||
| 172 | return ResultAdvertiseDataTooLarge; | ||
| 173 | } | ||
| 174 | |||
| 175 | std::memcpy(network_info.ldn.advertise_data.data(), data.data(), size); | ||
| 176 | network_info.ldn.advertise_data_size = static_cast<u16>(size); | ||
| 177 | |||
| 178 | UpdateNodes(); | ||
| 179 | |||
| 180 | return ResultSuccess; | ||
| 181 | } | ||
| 182 | |||
| 183 | Result LANDiscovery::OpenAccessPoint() { | ||
| 184 | std::scoped_lock lock{packet_mutex}; | ||
| 185 | disconnect_reason = DisconnectReason::None; | ||
| 186 | if (state == State::None) { | ||
| 187 | return ResultBadState; | ||
| 188 | } | ||
| 189 | |||
| 190 | ResetStations(); | ||
| 191 | SetState(State::AccessPointOpened); | ||
| 192 | |||
| 193 | return ResultSuccess; | ||
| 194 | } | ||
| 195 | |||
| 196 | Result LANDiscovery::CloseAccessPoint() { | ||
| 197 | std::scoped_lock lock{packet_mutex}; | ||
| 198 | if (state == State::None) { | ||
| 199 | return ResultBadState; | ||
| 200 | } | ||
| 201 | |||
| 202 | if (state == State::AccessPointCreated) { | ||
| 203 | DestroyNetwork(); | ||
| 204 | } | ||
| 205 | |||
| 206 | ResetStations(); | ||
| 207 | SetState(State::Initialized); | ||
| 208 | |||
| 209 | return ResultSuccess; | ||
| 210 | } | ||
| 211 | |||
| 212 | Result LANDiscovery::OpenStation() { | ||
| 213 | std::scoped_lock lock{packet_mutex}; | ||
| 214 | disconnect_reason = DisconnectReason::None; | ||
| 215 | if (state == State::None) { | ||
| 216 | return ResultBadState; | ||
| 217 | } | ||
| 218 | |||
| 219 | ResetStations(); | ||
| 220 | SetState(State::StationOpened); | ||
| 221 | |||
| 222 | return ResultSuccess; | ||
| 223 | } | ||
| 224 | |||
| 225 | Result LANDiscovery::CloseStation() { | ||
| 226 | std::scoped_lock lock{packet_mutex}; | ||
| 227 | if (state == State::None) { | ||
| 228 | return ResultBadState; | ||
| 229 | } | ||
| 230 | |||
| 231 | if (state == State::StationConnected) { | ||
| 232 | Disconnect(); | ||
| 233 | } | ||
| 234 | |||
| 235 | ResetStations(); | ||
| 236 | SetState(State::Initialized); | ||
| 237 | |||
| 238 | return ResultSuccess; | ||
| 239 | } | ||
| 240 | |||
| 241 | Result LANDiscovery::CreateNetwork(const SecurityConfig& security_config, | ||
| 242 | const UserConfig& user_config, | ||
| 243 | const NetworkConfig& network_config) { | ||
| 244 | std::scoped_lock lock{packet_mutex}; | ||
| 245 | |||
| 246 | if (state != State::AccessPointOpened) { | ||
| 247 | return ResultBadState; | ||
| 248 | } | ||
| 249 | |||
| 250 | InitNetworkInfo(); | ||
| 251 | network_info.ldn.node_count_max = network_config.node_count_max; | ||
| 252 | network_info.ldn.security_mode = security_config.security_mode; | ||
| 253 | |||
| 254 | if (network_config.channel == WifiChannel::Default) { | ||
| 255 | network_info.common.channel = WifiChannel::Wifi24_6; | ||
| 256 | } else { | ||
| 257 | network_info.common.channel = network_config.channel; | ||
| 258 | } | ||
| 259 | |||
| 260 | std::independent_bits_engine<std::mt19937, 64, u64> bits_engine; | ||
| 261 | network_info.network_id.session_id.high = bits_engine(); | ||
| 262 | network_info.network_id.session_id.low = bits_engine(); | ||
| 263 | network_info.network_id.intent_id = network_config.intent_id; | ||
| 264 | |||
| 265 | NodeInfo& node0 = network_info.ldn.nodes[0]; | ||
| 266 | const Result rc2 = GetNodeInfo(node0, user_config, network_config.local_communication_version); | ||
| 267 | if (rc2.IsError()) { | ||
| 268 | return ResultAccessPointConnectionFailed; | ||
| 269 | } | ||
| 270 | |||
| 271 | SetState(State::AccessPointCreated); | ||
| 272 | |||
| 273 | InitNodeStateChange(); | ||
| 274 | node0.is_connected = 1; | ||
| 275 | UpdateNodes(); | ||
| 276 | |||
| 277 | return rc2; | ||
| 278 | } | ||
| 279 | |||
| 280 | Result LANDiscovery::DestroyNetwork() { | ||
| 281 | for (auto local_ip : connected_clients) { | ||
| 282 | SendPacket(Network::LDNPacketType::DestroyNetwork, local_ip); | ||
| 283 | } | ||
| 284 | |||
| 285 | ResetStations(); | ||
| 286 | |||
| 287 | SetState(State::AccessPointOpened); | ||
| 288 | lan_event(); | ||
| 289 | |||
| 290 | return ResultSuccess; | ||
| 291 | } | ||
| 292 | |||
| 293 | Result LANDiscovery::Connect(const NetworkInfo& network_info_, const UserConfig& user_config, | ||
| 294 | u16 local_communication_version) { | ||
| 295 | std::scoped_lock lock{packet_mutex}; | ||
| 296 | if (network_info_.ldn.node_count == 0) { | ||
| 297 | return ResultInvalidNodeCount; | ||
| 298 | } | ||
| 299 | |||
| 300 | Result rc = GetNodeInfo(node_info, user_config, local_communication_version); | ||
| 301 | if (rc.IsError()) { | ||
| 302 | return ResultConnectionFailed; | ||
| 303 | } | ||
| 304 | |||
| 305 | Ipv4Address node_host = network_info_.ldn.nodes[0].ipv4_address; | ||
| 306 | std::reverse(std::begin(node_host), std::end(node_host)); // htonl | ||
| 307 | host_ip = node_host; | ||
| 308 | SendPacket(Network::LDNPacketType::Connect, node_info, *host_ip); | ||
| 309 | |||
| 310 | InitNodeStateChange(); | ||
| 311 | |||
| 312 | std::this_thread::sleep_for(std::chrono::seconds(1)); | ||
| 313 | |||
| 314 | return ResultSuccess; | ||
| 315 | } | ||
| 316 | |||
| 317 | Result LANDiscovery::Disconnect() { | ||
| 318 | if (host_ip) { | ||
| 319 | SendPacket(Network::LDNPacketType::Disconnect, node_info, *host_ip); | ||
| 320 | } | ||
| 321 | |||
| 322 | SetState(State::StationOpened); | ||
| 323 | lan_event(); | ||
| 324 | |||
| 325 | return ResultSuccess; | ||
| 326 | } | ||
| 327 | |||
| 328 | Result LANDiscovery::Initialize(LanEventFunc lan_event_, bool listening) { | ||
| 329 | std::scoped_lock lock{packet_mutex}; | ||
| 330 | if (inited) { | ||
| 331 | return ResultSuccess; | ||
| 332 | } | ||
| 333 | |||
| 334 | for (auto& station : stations) { | ||
| 335 | station.discovery = this; | ||
| 336 | station.node_info = &network_info.ldn.nodes[station.node_id]; | ||
| 337 | station.Reset(); | ||
| 338 | } | ||
| 339 | |||
| 340 | connected_clients.clear(); | ||
| 341 | lan_event = lan_event_; | ||
| 342 | |||
| 343 | SetState(State::Initialized); | ||
| 344 | |||
| 345 | inited = true; | ||
| 346 | return ResultSuccess; | ||
| 347 | } | ||
| 348 | |||
| 349 | Result LANDiscovery::Finalize() { | ||
| 350 | std::scoped_lock lock{packet_mutex}; | ||
| 351 | Result rc = ResultSuccess; | ||
| 352 | |||
| 353 | if (inited) { | ||
| 354 | if (state == State::AccessPointCreated) { | ||
| 355 | DestroyNetwork(); | ||
| 356 | } | ||
| 357 | if (state == State::StationConnected) { | ||
| 358 | Disconnect(); | ||
| 359 | } | ||
| 360 | |||
| 361 | ResetStations(); | ||
| 362 | inited = false; | ||
| 363 | } | ||
| 364 | |||
| 365 | SetState(State::None); | ||
| 366 | |||
| 367 | return rc; | ||
| 368 | } | ||
| 369 | |||
| 370 | void LANDiscovery::ResetStations() { | ||
| 371 | for (auto& station : stations) { | ||
| 372 | station.Reset(); | ||
| 373 | } | ||
| 374 | connected_clients.clear(); | ||
| 375 | } | ||
| 376 | |||
| 377 | void LANDiscovery::UpdateNodes() { | ||
| 378 | u8 count = 0; | ||
| 379 | for (auto& station : stations) { | ||
| 380 | bool connected = station.GetStatus() == NodeStatus::Connected; | ||
| 381 | if (connected) { | ||
| 382 | count++; | ||
| 383 | } | ||
| 384 | station.OverrideInfo(); | ||
| 385 | } | ||
| 386 | network_info.ldn.node_count = count + 1; | ||
| 387 | |||
| 388 | for (auto local_ip : connected_clients) { | ||
| 389 | SendPacket(Network::LDNPacketType::SyncNetwork, network_info, local_ip); | ||
| 390 | } | ||
| 391 | |||
| 392 | OnNetworkInfoChanged(); | ||
| 393 | } | ||
| 394 | |||
| 395 | void LANDiscovery::OnSyncNetwork(const NetworkInfo& info) { | ||
| 396 | network_info = info; | ||
| 397 | if (state == State::StationOpened) { | ||
| 398 | SetState(State::StationConnected); | ||
| 399 | } | ||
| 400 | OnNetworkInfoChanged(); | ||
| 401 | } | ||
| 402 | |||
| 403 | void LANDiscovery::OnDisconnectFromHost() { | ||
| 404 | LOG_INFO(Service_LDN, "OnDisconnectFromHost state: {}", static_cast<int>(state)); | ||
| 405 | host_ip = std::nullopt; | ||
| 406 | if (state == State::StationConnected) { | ||
| 407 | SetState(State::StationOpened); | ||
| 408 | lan_event(); | ||
| 409 | } | ||
| 410 | } | ||
| 411 | |||
| 412 | void LANDiscovery::OnNetworkInfoChanged() { | ||
| 413 | if (IsNodeStateChanged()) { | ||
| 414 | lan_event(); | ||
| 415 | } | ||
| 416 | return; | ||
| 417 | } | ||
| 418 | |||
| 419 | Network::IPv4Address LANDiscovery::GetLocalIp() const { | ||
| 420 | Network::IPv4Address local_ip{0xFF, 0xFF, 0xFF, 0xFF}; | ||
| 421 | if (auto room_member = room_network.GetRoomMember().lock()) { | ||
| 422 | if (room_member->IsConnected()) { | ||
| 423 | local_ip = room_member->GetFakeIpAddress(); | ||
| 424 | } | ||
| 425 | } | ||
| 426 | return local_ip; | ||
| 427 | } | ||
| 428 | |||
| 429 | template <typename Data> | ||
| 430 | void LANDiscovery::SendPacket(Network::LDNPacketType type, const Data& data, | ||
| 431 | Ipv4Address remote_ip) { | ||
| 432 | Network::LDNPacket packet; | ||
| 433 | packet.type = type; | ||
| 434 | |||
| 435 | packet.broadcast = false; | ||
| 436 | packet.local_ip = GetLocalIp(); | ||
| 437 | packet.remote_ip = remote_ip; | ||
| 438 | |||
| 439 | packet.data.resize(sizeof(data)); | ||
| 440 | std::memcpy(packet.data.data(), &data, sizeof(data)); | ||
| 441 | SendPacket(packet); | ||
| 442 | } | ||
| 443 | |||
| 444 | void LANDiscovery::SendPacket(Network::LDNPacketType type, Ipv4Address remote_ip) { | ||
| 445 | Network::LDNPacket packet; | ||
| 446 | packet.type = type; | ||
| 447 | |||
| 448 | packet.broadcast = false; | ||
| 449 | packet.local_ip = GetLocalIp(); | ||
| 450 | packet.remote_ip = remote_ip; | ||
| 451 | |||
| 452 | SendPacket(packet); | ||
| 453 | } | ||
| 454 | |||
| 455 | template <typename Data> | ||
| 456 | void LANDiscovery::SendBroadcast(Network::LDNPacketType type, const Data& data) { | ||
| 457 | Network::LDNPacket packet; | ||
| 458 | packet.type = type; | ||
| 459 | |||
| 460 | packet.broadcast = true; | ||
| 461 | packet.local_ip = GetLocalIp(); | ||
| 462 | |||
| 463 | packet.data.resize(sizeof(data)); | ||
| 464 | std::memcpy(packet.data.data(), &data, sizeof(data)); | ||
| 465 | SendPacket(packet); | ||
| 466 | } | ||
| 467 | |||
| 468 | void LANDiscovery::SendBroadcast(Network::LDNPacketType type) { | ||
| 469 | Network::LDNPacket packet; | ||
| 470 | packet.type = type; | ||
| 471 | |||
| 472 | packet.broadcast = true; | ||
| 473 | packet.local_ip = GetLocalIp(); | ||
| 474 | |||
| 475 | SendPacket(packet); | ||
| 476 | } | ||
| 477 | |||
| 478 | void LANDiscovery::SendPacket(const Network::LDNPacket& packet) { | ||
| 479 | if (auto room_member = room_network.GetRoomMember().lock()) { | ||
| 480 | if (room_member->IsConnected()) { | ||
| 481 | room_member->SendLdnPacket(packet); | ||
| 482 | } | ||
| 483 | } | ||
| 484 | } | ||
| 485 | |||
| 486 | void LANDiscovery::ReceivePacket(const Network::LDNPacket& packet) { | ||
| 487 | std::scoped_lock lock{packet_mutex}; | ||
| 488 | switch (packet.type) { | ||
| 489 | case Network::LDNPacketType::Scan: { | ||
| 490 | LOG_INFO(Frontend, "Scan packet received!"); | ||
| 491 | if (state == State::AccessPointCreated) { | ||
| 492 | // Reply to the sender | ||
| 493 | SendPacket(Network::LDNPacketType::ScanResp, network_info, packet.local_ip); | ||
| 494 | } | ||
| 495 | break; | ||
| 496 | } | ||
| 497 | case Network::LDNPacketType::ScanResp: { | ||
| 498 | LOG_INFO(Frontend, "ScanResp packet received!"); | ||
| 499 | |||
| 500 | NetworkInfo info{}; | ||
| 501 | std::memcpy(&info, packet.data.data(), sizeof(NetworkInfo)); | ||
| 502 | scan_results.insert({info.common.bssid, info}); | ||
| 503 | |||
| 504 | break; | ||
| 505 | } | ||
| 506 | case Network::LDNPacketType::Connect: { | ||
| 507 | LOG_INFO(Frontend, "Connect packet received!"); | ||
| 508 | |||
| 509 | NodeInfo info{}; | ||
| 510 | std::memcpy(&info, packet.data.data(), sizeof(NodeInfo)); | ||
| 511 | |||
| 512 | connected_clients.push_back(packet.local_ip); | ||
| 513 | |||
| 514 | for (LanStation& station : stations) { | ||
| 515 | if (station.status != NodeStatus::Connected) { | ||
| 516 | *station.node_info = info; | ||
| 517 | station.status = NodeStatus::Connected; | ||
| 518 | break; | ||
| 519 | } | ||
| 520 | } | ||
| 521 | |||
| 522 | UpdateNodes(); | ||
| 523 | |||
| 524 | break; | ||
| 525 | } | ||
| 526 | case Network::LDNPacketType::Disconnect: { | ||
| 527 | LOG_INFO(Frontend, "Disconnect packet received!"); | ||
| 528 | |||
| 529 | connected_clients.erase( | ||
| 530 | std::remove(connected_clients.begin(), connected_clients.end(), packet.local_ip), | ||
| 531 | connected_clients.end()); | ||
| 532 | |||
| 533 | NodeInfo info{}; | ||
| 534 | std::memcpy(&info, packet.data.data(), sizeof(NodeInfo)); | ||
| 535 | |||
| 536 | for (LanStation& station : stations) { | ||
| 537 | if (station.status == NodeStatus::Connected && | ||
| 538 | station.node_info->mac_address == info.mac_address) { | ||
| 539 | station.OnClose(); | ||
| 540 | break; | ||
| 541 | } | ||
| 542 | } | ||
| 543 | |||
| 544 | break; | ||
| 545 | } | ||
| 546 | case Network::LDNPacketType::DestroyNetwork: { | ||
| 547 | ResetStations(); | ||
| 548 | OnDisconnectFromHost(); | ||
| 549 | break; | ||
| 550 | } | ||
| 551 | case Network::LDNPacketType::SyncNetwork: { | ||
| 552 | if (state == State::StationOpened || state == State::StationConnected) { | ||
| 553 | LOG_INFO(Frontend, "SyncNetwork packet received!"); | ||
| 554 | NetworkInfo info{}; | ||
| 555 | std::memcpy(&info, packet.data.data(), sizeof(NetworkInfo)); | ||
| 556 | |||
| 557 | OnSyncNetwork(info); | ||
| 558 | } else { | ||
| 559 | LOG_INFO(Frontend, "SyncNetwork packet received but in wrong State!"); | ||
| 560 | } | ||
| 561 | |||
| 562 | break; | ||
| 563 | } | ||
| 564 | default: { | ||
| 565 | LOG_INFO(Frontend, "ReceivePacket unhandled type {}", static_cast<int>(packet.type)); | ||
| 566 | break; | ||
| 567 | } | ||
| 568 | } | ||
| 569 | } | ||
| 570 | |||
| 571 | bool LANDiscovery::IsNodeStateChanged() { | ||
| 572 | bool changed = false; | ||
| 573 | const auto& nodes = network_info.ldn.nodes; | ||
| 574 | for (int i = 0; i < NodeCountMax; i++) { | ||
| 575 | if (nodes[i].is_connected != node_last_states[i]) { | ||
| 576 | if (nodes[i].is_connected) { | ||
| 577 | node_changes[i].state_change |= NodeStateChange::Connect; | ||
| 578 | } else { | ||
| 579 | node_changes[i].state_change |= NodeStateChange::Disconnect; | ||
| 580 | } | ||
| 581 | node_last_states[i] = nodes[i].is_connected; | ||
| 582 | changed = true; | ||
| 583 | } | ||
| 584 | } | ||
| 585 | return changed; | ||
| 586 | } | ||
| 587 | |||
| 588 | bool LANDiscovery::IsFlagSet(ScanFilterFlag flag, ScanFilterFlag search_flag) const { | ||
| 589 | const auto flag_value = static_cast<u32>(flag); | ||
| 590 | const auto search_flag_value = static_cast<u32>(search_flag); | ||
| 591 | return (flag_value & search_flag_value) == search_flag_value; | ||
| 592 | } | ||
| 593 | |||
| 594 | int LANDiscovery::GetStationCount() const { | ||
| 595 | return static_cast<int>( | ||
| 596 | std::count_if(stations.begin(), stations.end(), [](const auto& station) { | ||
| 597 | return station.GetStatus() != NodeStatus::Disconnected; | ||
| 598 | })); | ||
| 599 | } | ||
| 600 | |||
| 601 | MacAddress LANDiscovery::GetFakeMac() const { | ||
| 602 | MacAddress mac{}; | ||
| 603 | mac.raw[0] = 0x02; | ||
| 604 | mac.raw[1] = 0x00; | ||
| 605 | |||
| 606 | const auto ip = GetLocalIp(); | ||
| 607 | memcpy(mac.raw.data() + 2, &ip, sizeof(ip)); | ||
| 608 | |||
| 609 | return mac; | ||
| 610 | } | ||
| 611 | |||
| 612 | Result LANDiscovery::GetNodeInfo(NodeInfo& node, const UserConfig& userConfig, | ||
| 613 | u16 localCommunicationVersion) { | ||
| 614 | const auto network_interface = Network::GetSelectedNetworkInterface(); | ||
| 615 | |||
| 616 | if (!network_interface) { | ||
| 617 | LOG_ERROR(Service_LDN, "No network interface available"); | ||
| 618 | return ResultNoIpAddress; | ||
| 619 | } | ||
| 620 | |||
| 621 | node.mac_address = GetFakeMac(); | ||
| 622 | node.is_connected = 1; | ||
| 623 | std::memcpy(node.user_name.data(), userConfig.user_name.data(), UserNameBytesMax + 1); | ||
| 624 | node.local_communication_version = localCommunicationVersion; | ||
| 625 | |||
| 626 | Ipv4Address current_address = GetLocalIp(); | ||
| 627 | std::reverse(std::begin(current_address), std::end(current_address)); // ntohl | ||
| 628 | node.ipv4_address = current_address; | ||
| 629 | |||
| 630 | return ResultSuccess; | ||
| 631 | } | ||
| 632 | |||
| 633 | } // 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 000000000..3833cd764 --- /dev/null +++ b/src/core/hle/service/ldn/lan_discovery.h | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <cstring> | ||
| 8 | #include <functional> | ||
| 9 | #include <memory> | ||
| 10 | #include <mutex> | ||
| 11 | #include <optional> | ||
| 12 | #include <random> | ||
| 13 | #include <span> | ||
| 14 | #include <thread> | ||
| 15 | #include <unordered_map> | ||
| 16 | |||
| 17 | #include "common/logging/log.h" | ||
| 18 | #include "common/socket_types.h" | ||
| 19 | #include "core/hle/result.h" | ||
| 20 | #include "core/hle/service/ldn/ldn_results.h" | ||
| 21 | #include "core/hle/service/ldn/ldn_types.h" | ||
| 22 | #include "network/network.h" | ||
| 23 | |||
| 24 | namespace Service::LDN { | ||
| 25 | |||
| 26 | class LANDiscovery; | ||
| 27 | |||
| 28 | class LanStation { | ||
| 29 | public: | ||
| 30 | LanStation(s8 node_id_, LANDiscovery* discovery_); | ||
| 31 | ~LanStation(); | ||
| 32 | |||
| 33 | void OnClose(); | ||
| 34 | NodeStatus GetStatus() const; | ||
| 35 | void Reset(); | ||
| 36 | void OverrideInfo(); | ||
| 37 | |||
| 38 | protected: | ||
| 39 | friend class LANDiscovery; | ||
| 40 | NodeInfo* node_info; | ||
| 41 | NodeStatus status; | ||
| 42 | s8 node_id; | ||
| 43 | LANDiscovery* discovery; | ||
| 44 | }; | ||
| 45 | |||
| 46 | class LANDiscovery { | ||
| 47 | public: | ||
| 48 | using LanEventFunc = std::function<void()>; | ||
| 49 | |||
| 50 | LANDiscovery(Network::RoomNetwork& room_network_); | ||
| 51 | ~LANDiscovery(); | ||
| 52 | |||
| 53 | State GetState() const; | ||
| 54 | void SetState(State new_state); | ||
| 55 | |||
| 56 | Result GetNetworkInfo(NetworkInfo& out_network) const; | ||
| 57 | Result GetNetworkInfo(NetworkInfo& out_network, std::vector<NodeLatestUpdate>& out_updates, | ||
| 58 | std::size_t buffer_count); | ||
| 59 | |||
| 60 | DisconnectReason GetDisconnectReason() const; | ||
| 61 | Result Scan(std::vector<NetworkInfo>& networks, u16& count, const ScanFilter& filter); | ||
| 62 | Result SetAdvertiseData(std::span<const u8> data); | ||
| 63 | |||
| 64 | Result OpenAccessPoint(); | ||
| 65 | Result CloseAccessPoint(); | ||
| 66 | |||
| 67 | Result OpenStation(); | ||
| 68 | Result CloseStation(); | ||
| 69 | |||
| 70 | Result CreateNetwork(const SecurityConfig& security_config, const UserConfig& user_config, | ||
| 71 | const NetworkConfig& network_config); | ||
| 72 | Result DestroyNetwork(); | ||
| 73 | |||
| 74 | Result Connect(const NetworkInfo& network_info_, const UserConfig& user_config, | ||
| 75 | u16 local_communication_version); | ||
| 76 | Result Disconnect(); | ||
| 77 | |||
| 78 | Result Initialize(LanEventFunc lan_event_ = empty_func, bool listening = true); | ||
| 79 | Result Finalize(); | ||
| 80 | |||
| 81 | void ReceivePacket(const Network::LDNPacket& packet); | ||
| 82 | |||
| 83 | protected: | ||
| 84 | friend class LanStation; | ||
| 85 | |||
| 86 | void InitNetworkInfo(); | ||
| 87 | void InitNodeStateChange(); | ||
| 88 | |||
| 89 | void ResetStations(); | ||
| 90 | void UpdateNodes(); | ||
| 91 | |||
| 92 | void OnSyncNetwork(const NetworkInfo& info); | ||
| 93 | void OnDisconnectFromHost(); | ||
| 94 | void OnNetworkInfoChanged(); | ||
| 95 | |||
| 96 | bool IsNodeStateChanged(); | ||
| 97 | bool IsFlagSet(ScanFilterFlag flag, ScanFilterFlag search_flag) const; | ||
| 98 | int GetStationCount() const; | ||
| 99 | MacAddress GetFakeMac() const; | ||
| 100 | Result GetNodeInfo(NodeInfo& node, const UserConfig& user_config, | ||
| 101 | u16 local_communication_version); | ||
| 102 | |||
| 103 | Network::IPv4Address GetLocalIp() const; | ||
| 104 | template <typename Data> | ||
| 105 | void SendPacket(Network::LDNPacketType type, const Data& data, Ipv4Address remote_ip); | ||
| 106 | void SendPacket(Network::LDNPacketType type, Ipv4Address remote_ip); | ||
| 107 | template <typename Data> | ||
| 108 | void SendBroadcast(Network::LDNPacketType type, const Data& data); | ||
| 109 | void SendBroadcast(Network::LDNPacketType type); | ||
| 110 | void SendPacket(const Network::LDNPacket& packet); | ||
| 111 | |||
| 112 | static const LanEventFunc empty_func; | ||
| 113 | static constexpr Ssid fake_ssid{"YuzuFakeSsidForLdn"}; | ||
| 114 | |||
| 115 | bool inited{}; | ||
| 116 | std::mutex packet_mutex; | ||
| 117 | std::array<LanStation, StationCountMax> stations; | ||
| 118 | std::array<NodeLatestUpdate, NodeCountMax> node_changes{}; | ||
| 119 | std::array<u8, NodeCountMax> node_last_states{}; | ||
| 120 | std::unordered_map<MacAddress, NetworkInfo, MACAddressHash> scan_results{}; | ||
| 121 | NodeInfo node_info{}; | ||
| 122 | NetworkInfo network_info{}; | ||
| 123 | State state{State::None}; | ||
| 124 | DisconnectReason disconnect_reason{DisconnectReason::None}; | ||
| 125 | |||
| 126 | // TODO (flTobi): Should this be an std::set? | ||
| 127 | std::vector<Ipv4Address> connected_clients; | ||
| 128 | std::optional<Ipv4Address> host_ip; | ||
| 129 | |||
| 130 | LanEventFunc lan_event; | ||
| 131 | |||
| 132 | Network::RoomNetwork& room_network; | ||
| 133 | }; | ||
| 134 | } // namespace Service::LDN | ||
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp index c11daff54..ea3e7e55a 100644 --- a/src/core/hle/service/ldn/ldn.cpp +++ b/src/core/hle/service/ldn/ldn.cpp | |||
| @@ -4,11 +4,13 @@ | |||
| 4 | #include <memory> | 4 | #include <memory> |
| 5 | 5 | ||
| 6 | #include "core/core.h" | 6 | #include "core/core.h" |
| 7 | #include "core/hle/service/ldn/lan_discovery.h" | ||
| 7 | #include "core/hle/service/ldn/ldn.h" | 8 | #include "core/hle/service/ldn/ldn.h" |
| 8 | #include "core/hle/service/ldn/ldn_results.h" | 9 | #include "core/hle/service/ldn/ldn_results.h" |
| 9 | #include "core/hle/service/ldn/ldn_types.h" | 10 | #include "core/hle/service/ldn/ldn_types.h" |
| 10 | #include "core/internal_network/network.h" | 11 | #include "core/internal_network/network.h" |
| 11 | #include "core/internal_network/network_interface.h" | 12 | #include "core/internal_network/network_interface.h" |
| 13 | #include "network/network.h" | ||
| 12 | 14 | ||
| 13 | // This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent | 15 | // This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent |
| 14 | #undef CreateEvent | 16 | #undef CreateEvent |
| @@ -105,13 +107,13 @@ class IUserLocalCommunicationService final | |||
| 105 | public: | 107 | public: |
| 106 | explicit IUserLocalCommunicationService(Core::System& system_) | 108 | explicit IUserLocalCommunicationService(Core::System& system_) |
| 107 | : ServiceFramework{system_, "IUserLocalCommunicationService", ServiceThreadType::CreateNew}, | 109 | : ServiceFramework{system_, "IUserLocalCommunicationService", ServiceThreadType::CreateNew}, |
| 108 | service_context{system, "IUserLocalCommunicationService"}, room_network{ | 110 | service_context{system, "IUserLocalCommunicationService"}, |
| 109 | system_.GetRoomNetwork()} { | 111 | room_network{system_.GetRoomNetwork()}, lan_discovery{room_network} { |
| 110 | // clang-format off | 112 | // clang-format off |
| 111 | static const FunctionInfo functions[] = { | 113 | static const FunctionInfo functions[] = { |
| 112 | {0, &IUserLocalCommunicationService::GetState, "GetState"}, | 114 | {0, &IUserLocalCommunicationService::GetState, "GetState"}, |
| 113 | {1, &IUserLocalCommunicationService::GetNetworkInfo, "GetNetworkInfo"}, | 115 | {1, &IUserLocalCommunicationService::GetNetworkInfo, "GetNetworkInfo"}, |
| 114 | {2, nullptr, "GetIpv4Address"}, | 116 | {2, &IUserLocalCommunicationService::GetIpv4Address, "GetIpv4Address"}, |
| 115 | {3, &IUserLocalCommunicationService::GetDisconnectReason, "GetDisconnectReason"}, | 117 | {3, &IUserLocalCommunicationService::GetDisconnectReason, "GetDisconnectReason"}, |
| 116 | {4, &IUserLocalCommunicationService::GetSecurityParameter, "GetSecurityParameter"}, | 118 | {4, &IUserLocalCommunicationService::GetSecurityParameter, "GetSecurityParameter"}, |
| 117 | {5, &IUserLocalCommunicationService::GetNetworkConfig, "GetNetworkConfig"}, | 119 | {5, &IUserLocalCommunicationService::GetNetworkConfig, "GetNetworkConfig"}, |
| @@ -119,7 +121,7 @@ public: | |||
| 119 | {101, &IUserLocalCommunicationService::GetNetworkInfoLatestUpdate, "GetNetworkInfoLatestUpdate"}, | 121 | {101, &IUserLocalCommunicationService::GetNetworkInfoLatestUpdate, "GetNetworkInfoLatestUpdate"}, |
| 120 | {102, &IUserLocalCommunicationService::Scan, "Scan"}, | 122 | {102, &IUserLocalCommunicationService::Scan, "Scan"}, |
| 121 | {103, &IUserLocalCommunicationService::ScanPrivate, "ScanPrivate"}, | 123 | {103, &IUserLocalCommunicationService::ScanPrivate, "ScanPrivate"}, |
| 122 | {104, nullptr, "SetWirelessControllerRestriction"}, | 124 | {104, &IUserLocalCommunicationService::SetWirelessControllerRestriction, "SetWirelessControllerRestriction"}, |
| 123 | {200, &IUserLocalCommunicationService::OpenAccessPoint, "OpenAccessPoint"}, | 125 | {200, &IUserLocalCommunicationService::OpenAccessPoint, "OpenAccessPoint"}, |
| 124 | {201, &IUserLocalCommunicationService::CloseAccessPoint, "CloseAccessPoint"}, | 126 | {201, &IUserLocalCommunicationService::CloseAccessPoint, "CloseAccessPoint"}, |
| 125 | {202, &IUserLocalCommunicationService::CreateNetwork, "CreateNetwork"}, | 127 | {202, &IUserLocalCommunicationService::CreateNetwork, "CreateNetwork"}, |
| @@ -148,16 +150,30 @@ public: | |||
| 148 | } | 150 | } |
| 149 | 151 | ||
| 150 | ~IUserLocalCommunicationService() { | 152 | ~IUserLocalCommunicationService() { |
| 153 | if (is_initialized) { | ||
| 154 | if (auto room_member = room_network.GetRoomMember().lock()) { | ||
| 155 | room_member->Unbind(ldn_packet_received); | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 151 | service_context.CloseEvent(state_change_event); | 159 | service_context.CloseEvent(state_change_event); |
| 152 | } | 160 | } |
| 153 | 161 | ||
| 162 | /// Callback to parse and handle a received LDN packet. | ||
| 163 | void OnLDNPacketReceived(const Network::LDNPacket& packet) { | ||
| 164 | lan_discovery.ReceivePacket(packet); | ||
| 165 | } | ||
| 166 | |||
| 154 | void OnEventFired() { | 167 | void OnEventFired() { |
| 155 | state_change_event->GetWritableEvent().Signal(); | 168 | state_change_event->GetWritableEvent().Signal(); |
| 156 | } | 169 | } |
| 157 | 170 | ||
| 158 | void GetState(Kernel::HLERequestContext& ctx) { | 171 | void GetState(Kernel::HLERequestContext& ctx) { |
| 159 | State state = State::Error; | 172 | State state = State::Error; |
| 160 | LOG_WARNING(Service_LDN, "(STUBBED) called, state = {}", state); | 173 | |
| 174 | if (is_initialized) { | ||
| 175 | state = lan_discovery.GetState(); | ||
| 176 | } | ||
| 161 | 177 | ||
| 162 | IPC::ResponseBuilder rb{ctx, 3}; | 178 | IPC::ResponseBuilder rb{ctx, 3}; |
| 163 | rb.Push(ResultSuccess); | 179 | rb.Push(ResultSuccess); |
| @@ -175,7 +191,7 @@ public: | |||
| 175 | } | 191 | } |
| 176 | 192 | ||
| 177 | NetworkInfo network_info{}; | 193 | NetworkInfo network_info{}; |
| 178 | const auto rc = ResultSuccess; | 194 | const auto rc = lan_discovery.GetNetworkInfo(network_info); |
| 179 | if (rc.IsError()) { | 195 | if (rc.IsError()) { |
| 180 | LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); | 196 | LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); |
| 181 | IPC::ResponseBuilder rb{ctx, 2}; | 197 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -183,28 +199,50 @@ public: | |||
| 183 | return; | 199 | return; |
| 184 | } | 200 | } |
| 185 | 201 | ||
| 186 | LOG_WARNING(Service_LDN, "(STUBBED) called, ssid='{}', nodes={}", | ||
| 187 | network_info.common.ssid.GetStringValue(), network_info.ldn.node_count); | ||
| 188 | |||
| 189 | ctx.WriteBuffer<NetworkInfo>(network_info); | 202 | ctx.WriteBuffer<NetworkInfo>(network_info); |
| 190 | IPC::ResponseBuilder rb{ctx, 2}; | 203 | IPC::ResponseBuilder rb{ctx, 2}; |
| 191 | rb.Push(rc); | 204 | rb.Push(ResultSuccess); |
| 192 | } | 205 | } |
| 193 | 206 | ||
| 194 | void GetDisconnectReason(Kernel::HLERequestContext& ctx) { | 207 | void GetIpv4Address(Kernel::HLERequestContext& ctx) { |
| 195 | const auto disconnect_reason = DisconnectReason::None; | 208 | const auto network_interface = Network::GetSelectedNetworkInterface(); |
| 209 | |||
| 210 | if (!network_interface) { | ||
| 211 | LOG_ERROR(Service_LDN, "No network interface available"); | ||
| 212 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 213 | rb.Push(ResultNoIpAddress); | ||
| 214 | return; | ||
| 215 | } | ||
| 196 | 216 | ||
| 197 | LOG_WARNING(Service_LDN, "(STUBBED) called, disconnect_reason={}", disconnect_reason); | 217 | Ipv4Address current_address{Network::TranslateIPv4(network_interface->ip_address)}; |
| 218 | Ipv4Address subnet_mask{Network::TranslateIPv4(network_interface->subnet_mask)}; | ||
| 219 | |||
| 220 | // When we're connected to a room, spoof the hosts IP address | ||
| 221 | if (auto room_member = room_network.GetRoomMember().lock()) { | ||
| 222 | if (room_member->IsConnected()) { | ||
| 223 | current_address = room_member->GetFakeIpAddress(); | ||
| 224 | } | ||
| 225 | } | ||
| 226 | |||
| 227 | std::reverse(std::begin(current_address), std::end(current_address)); // ntohl | ||
| 228 | std::reverse(std::begin(subnet_mask), std::end(subnet_mask)); // ntohl | ||
| 229 | |||
| 230 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 231 | rb.Push(ResultSuccess); | ||
| 232 | rb.PushRaw(current_address); | ||
| 233 | rb.PushRaw(subnet_mask); | ||
| 234 | } | ||
| 198 | 235 | ||
| 236 | void GetDisconnectReason(Kernel::HLERequestContext& ctx) { | ||
| 199 | IPC::ResponseBuilder rb{ctx, 3}; | 237 | IPC::ResponseBuilder rb{ctx, 3}; |
| 200 | rb.Push(ResultSuccess); | 238 | rb.Push(ResultSuccess); |
| 201 | rb.PushEnum(disconnect_reason); | 239 | rb.PushEnum(lan_discovery.GetDisconnectReason()); |
| 202 | } | 240 | } |
| 203 | 241 | ||
| 204 | void GetSecurityParameter(Kernel::HLERequestContext& ctx) { | 242 | void GetSecurityParameter(Kernel::HLERequestContext& ctx) { |
| 205 | SecurityParameter security_parameter{}; | 243 | SecurityParameter security_parameter{}; |
| 206 | NetworkInfo info{}; | 244 | NetworkInfo info{}; |
| 207 | const Result rc = ResultSuccess; | 245 | const Result rc = lan_discovery.GetNetworkInfo(info); |
| 208 | 246 | ||
| 209 | if (rc.IsError()) { | 247 | if (rc.IsError()) { |
| 210 | LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); | 248 | LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); |
| @@ -217,8 +255,6 @@ public: | |||
| 217 | std::memcpy(security_parameter.data.data(), info.ldn.security_parameter.data(), | 255 | std::memcpy(security_parameter.data.data(), info.ldn.security_parameter.data(), |
| 218 | sizeof(SecurityParameter::data)); | 256 | sizeof(SecurityParameter::data)); |
| 219 | 257 | ||
| 220 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | ||
| 221 | |||
| 222 | IPC::ResponseBuilder rb{ctx, 10}; | 258 | IPC::ResponseBuilder rb{ctx, 10}; |
| 223 | rb.Push(rc); | 259 | rb.Push(rc); |
| 224 | rb.PushRaw<SecurityParameter>(security_parameter); | 260 | rb.PushRaw<SecurityParameter>(security_parameter); |
| @@ -227,7 +263,7 @@ public: | |||
| 227 | void GetNetworkConfig(Kernel::HLERequestContext& ctx) { | 263 | void GetNetworkConfig(Kernel::HLERequestContext& ctx) { |
| 228 | NetworkConfig config{}; | 264 | NetworkConfig config{}; |
| 229 | NetworkInfo info{}; | 265 | NetworkInfo info{}; |
| 230 | const Result rc = ResultSuccess; | 266 | const Result rc = lan_discovery.GetNetworkInfo(info); |
| 231 | 267 | ||
| 232 | if (rc.IsError()) { | 268 | if (rc.IsError()) { |
| 233 | LOG_ERROR(Service_LDN, "NetworkConfig is not valid {}", rc.raw); | 269 | LOG_ERROR(Service_LDN, "NetworkConfig is not valid {}", rc.raw); |
| @@ -241,12 +277,6 @@ public: | |||
| 241 | config.node_count_max = info.ldn.node_count_max; | 277 | config.node_count_max = info.ldn.node_count_max; |
| 242 | config.local_communication_version = info.ldn.nodes[0].local_communication_version; | 278 | config.local_communication_version = info.ldn.nodes[0].local_communication_version; |
| 243 | 279 | ||
| 244 | LOG_WARNING(Service_LDN, | ||
| 245 | "(STUBBED) called, intent_id={}/{}, channel={}, node_count_max={}, " | ||
| 246 | "local_communication_version={}", | ||
| 247 | config.intent_id.local_communication_id, config.intent_id.scene_id, | ||
| 248 | config.channel, config.node_count_max, config.local_communication_version); | ||
| 249 | |||
| 250 | IPC::ResponseBuilder rb{ctx, 10}; | 280 | IPC::ResponseBuilder rb{ctx, 10}; |
| 251 | rb.Push(rc); | 281 | rb.Push(rc); |
| 252 | rb.PushRaw<NetworkConfig>(config); | 282 | rb.PushRaw<NetworkConfig>(config); |
| @@ -265,17 +295,17 @@ public: | |||
| 265 | const std::size_t node_buffer_count = ctx.GetWriteBufferSize(1) / sizeof(NodeLatestUpdate); | 295 | const std::size_t node_buffer_count = ctx.GetWriteBufferSize(1) / sizeof(NodeLatestUpdate); |
| 266 | 296 | ||
| 267 | if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) { | 297 | if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) { |
| 268 | LOG_ERROR(Service_LDN, "Invalid buffer size {}, {}", network_buffer_size, | 298 | LOG_ERROR(Service_LDN, "Invalid buffer, size = {}, count = {}", network_buffer_size, |
| 269 | node_buffer_count); | 299 | node_buffer_count); |
| 270 | IPC::ResponseBuilder rb{ctx, 2}; | 300 | IPC::ResponseBuilder rb{ctx, 2}; |
| 271 | rb.Push(ResultBadInput); | 301 | rb.Push(ResultBadInput); |
| 272 | return; | 302 | return; |
| 273 | } | 303 | } |
| 274 | 304 | ||
| 275 | NetworkInfo info; | 305 | NetworkInfo info{}; |
| 276 | std::vector<NodeLatestUpdate> latest_update(node_buffer_count); | 306 | std::vector<NodeLatestUpdate> latest_update(node_buffer_count); |
| 277 | 307 | ||
| 278 | const auto rc = ResultSuccess; | 308 | const auto rc = lan_discovery.GetNetworkInfo(info, latest_update, latest_update.size()); |
| 279 | if (rc.IsError()) { | 309 | if (rc.IsError()) { |
| 280 | LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); | 310 | LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); |
| 281 | IPC::ResponseBuilder rb{ctx, 2}; | 311 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -283,9 +313,6 @@ public: | |||
| 283 | return; | 313 | return; |
| 284 | } | 314 | } |
| 285 | 315 | ||
| 286 | LOG_WARNING(Service_LDN, "(STUBBED) called, ssid='{}', nodes={}", | ||
| 287 | info.common.ssid.GetStringValue(), info.ldn.node_count); | ||
| 288 | |||
| 289 | ctx.WriteBuffer(info, 0); | 316 | ctx.WriteBuffer(info, 0); |
| 290 | ctx.WriteBuffer(latest_update, 1); | 317 | ctx.WriteBuffer(latest_update, 1); |
| 291 | 318 | ||
| @@ -317,92 +344,78 @@ public: | |||
| 317 | 344 | ||
| 318 | u16 count = 0; | 345 | u16 count = 0; |
| 319 | std::vector<NetworkInfo> network_infos(network_info_size); | 346 | std::vector<NetworkInfo> network_infos(network_info_size); |
| 347 | Result rc = lan_discovery.Scan(network_infos, count, scan_filter); | ||
| 320 | 348 | ||
| 321 | LOG_WARNING(Service_LDN, | 349 | LOG_INFO(Service_LDN, |
| 322 | "(STUBBED) called, channel={}, filter_scan_flag={}, filter_network_type={}", | 350 | "called, channel={}, filter_scan_flag={}, filter_network_type={}, is_private={}", |
| 323 | channel, scan_filter.flag, scan_filter.network_type); | 351 | channel, scan_filter.flag, scan_filter.network_type, is_private); |
| 324 | 352 | ||
| 325 | ctx.WriteBuffer(network_infos); | 353 | ctx.WriteBuffer(network_infos); |
| 326 | 354 | ||
| 327 | IPC::ResponseBuilder rb{ctx, 3}; | 355 | IPC::ResponseBuilder rb{ctx, 3}; |
| 328 | rb.Push(ResultSuccess); | 356 | rb.Push(rc); |
| 329 | rb.Push<u32>(count); | 357 | rb.Push<u32>(count); |
| 330 | } | 358 | } |
| 331 | 359 | ||
| 332 | void OpenAccessPoint(Kernel::HLERequestContext& ctx) { | 360 | void SetWirelessControllerRestriction(Kernel::HLERequestContext& ctx) { |
| 333 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | 361 | LOG_WARNING(Service_LDN, "(STUBBED) called"); |
| 334 | 362 | ||
| 335 | IPC::ResponseBuilder rb{ctx, 2}; | 363 | IPC::ResponseBuilder rb{ctx, 2}; |
| 336 | rb.Push(ResultSuccess); | 364 | rb.Push(ResultSuccess); |
| 337 | } | 365 | } |
| 338 | 366 | ||
| 367 | void OpenAccessPoint(Kernel::HLERequestContext& ctx) { | ||
| 368 | LOG_INFO(Service_LDN, "called"); | ||
| 369 | |||
| 370 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 371 | rb.Push(lan_discovery.OpenAccessPoint()); | ||
| 372 | } | ||
| 373 | |||
| 339 | void CloseAccessPoint(Kernel::HLERequestContext& ctx) { | 374 | void CloseAccessPoint(Kernel::HLERequestContext& ctx) { |
| 340 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | 375 | LOG_INFO(Service_LDN, "called"); |
| 341 | 376 | ||
| 342 | IPC::ResponseBuilder rb{ctx, 2}; | 377 | IPC::ResponseBuilder rb{ctx, 2}; |
| 343 | rb.Push(ResultSuccess); | 378 | rb.Push(lan_discovery.CloseAccessPoint()); |
| 344 | } | 379 | } |
| 345 | 380 | ||
| 346 | void CreateNetwork(Kernel::HLERequestContext& ctx) { | 381 | void CreateNetwork(Kernel::HLERequestContext& ctx) { |
| 347 | IPC::RequestParser rp{ctx}; | 382 | LOG_INFO(Service_LDN, "called"); |
| 348 | struct Parameters { | ||
| 349 | SecurityConfig security_config; | ||
| 350 | UserConfig user_config; | ||
| 351 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 352 | NetworkConfig network_config; | ||
| 353 | }; | ||
| 354 | static_assert(sizeof(Parameters) == 0x98, "Parameters has incorrect size."); | ||
| 355 | 383 | ||
| 356 | const auto parameters{rp.PopRaw<Parameters>()}; | 384 | CreateNetworkImpl(ctx); |
| 385 | } | ||
| 357 | 386 | ||
| 358 | LOG_WARNING(Service_LDN, | 387 | void CreateNetworkPrivate(Kernel::HLERequestContext& ctx) { |
| 359 | "(STUBBED) called, passphrase_size={}, security_mode={}, " | 388 | LOG_INFO(Service_LDN, "called"); |
| 360 | "local_communication_version={}", | ||
| 361 | parameters.security_config.passphrase_size, | ||
| 362 | parameters.security_config.security_mode, | ||
| 363 | parameters.network_config.local_communication_version); | ||
| 364 | 389 | ||
| 365 | IPC::ResponseBuilder rb{ctx, 2}; | 390 | CreateNetworkImpl(ctx, true); |
| 366 | rb.Push(ResultSuccess); | ||
| 367 | } | 391 | } |
| 368 | 392 | ||
| 369 | void CreateNetworkPrivate(Kernel::HLERequestContext& ctx) { | 393 | void CreateNetworkImpl(Kernel::HLERequestContext& ctx, bool is_private = false) { |
| 370 | IPC::RequestParser rp{ctx}; | 394 | IPC::RequestParser rp{ctx}; |
| 371 | struct Parameters { | ||
| 372 | SecurityConfig security_config; | ||
| 373 | SecurityParameter security_parameter; | ||
| 374 | UserConfig user_config; | ||
| 375 | NetworkConfig network_config; | ||
| 376 | }; | ||
| 377 | static_assert(sizeof(Parameters) == 0xB8, "Parameters has incorrect size."); | ||
| 378 | |||
| 379 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 380 | 395 | ||
| 381 | LOG_WARNING(Service_LDN, | 396 | const auto security_config{rp.PopRaw<SecurityConfig>()}; |
| 382 | "(STUBBED) called, passphrase_size={}, security_mode={}, " | 397 | [[maybe_unused]] const auto security_parameter{is_private ? rp.PopRaw<SecurityParameter>() |
| 383 | "local_communication_version={}", | 398 | : SecurityParameter{}}; |
| 384 | parameters.security_config.passphrase_size, | 399 | const auto user_config{rp.PopRaw<UserConfig>()}; |
| 385 | parameters.security_config.security_mode, | 400 | rp.Pop<u32>(); // Padding |
| 386 | parameters.network_config.local_communication_version); | 401 | const auto network_Config{rp.PopRaw<NetworkConfig>()}; |
| 387 | 402 | ||
| 388 | IPC::ResponseBuilder rb{ctx, 2}; | 403 | IPC::ResponseBuilder rb{ctx, 2}; |
| 389 | rb.Push(ResultSuccess); | 404 | rb.Push(lan_discovery.CreateNetwork(security_config, user_config, network_Config)); |
| 390 | } | 405 | } |
| 391 | 406 | ||
| 392 | void DestroyNetwork(Kernel::HLERequestContext& ctx) { | 407 | void DestroyNetwork(Kernel::HLERequestContext& ctx) { |
| 393 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | 408 | LOG_INFO(Service_LDN, "called"); |
| 394 | 409 | ||
| 395 | IPC::ResponseBuilder rb{ctx, 2}; | 410 | IPC::ResponseBuilder rb{ctx, 2}; |
| 396 | rb.Push(ResultSuccess); | 411 | rb.Push(lan_discovery.DestroyNetwork()); |
| 397 | } | 412 | } |
| 398 | 413 | ||
| 399 | void SetAdvertiseData(Kernel::HLERequestContext& ctx) { | 414 | void SetAdvertiseData(Kernel::HLERequestContext& ctx) { |
| 400 | std::vector<u8> read_buffer = ctx.ReadBuffer(); | 415 | std::vector<u8> read_buffer = ctx.ReadBuffer(); |
| 401 | 416 | ||
| 402 | LOG_WARNING(Service_LDN, "(STUBBED) called, size {}", read_buffer.size()); | ||
| 403 | |||
| 404 | IPC::ResponseBuilder rb{ctx, 2}; | 417 | IPC::ResponseBuilder rb{ctx, 2}; |
| 405 | rb.Push(ResultSuccess); | 418 | rb.Push(lan_discovery.SetAdvertiseData(read_buffer)); |
| 406 | } | 419 | } |
| 407 | 420 | ||
| 408 | void SetStationAcceptPolicy(Kernel::HLERequestContext& ctx) { | 421 | void SetStationAcceptPolicy(Kernel::HLERequestContext& ctx) { |
| @@ -420,17 +433,17 @@ public: | |||
| 420 | } | 433 | } |
| 421 | 434 | ||
| 422 | void OpenStation(Kernel::HLERequestContext& ctx) { | 435 | void OpenStation(Kernel::HLERequestContext& ctx) { |
| 423 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | 436 | LOG_INFO(Service_LDN, "called"); |
| 424 | 437 | ||
| 425 | IPC::ResponseBuilder rb{ctx, 2}; | 438 | IPC::ResponseBuilder rb{ctx, 2}; |
| 426 | rb.Push(ResultSuccess); | 439 | rb.Push(lan_discovery.OpenStation()); |
| 427 | } | 440 | } |
| 428 | 441 | ||
| 429 | void CloseStation(Kernel::HLERequestContext& ctx) { | 442 | void CloseStation(Kernel::HLERequestContext& ctx) { |
| 430 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | 443 | LOG_INFO(Service_LDN, "called"); |
| 431 | 444 | ||
| 432 | IPC::ResponseBuilder rb{ctx, 2}; | 445 | IPC::ResponseBuilder rb{ctx, 2}; |
| 433 | rb.Push(ResultSuccess); | 446 | rb.Push(lan_discovery.CloseStation()); |
| 434 | } | 447 | } |
| 435 | 448 | ||
| 436 | void Connect(Kernel::HLERequestContext& ctx) { | 449 | void Connect(Kernel::HLERequestContext& ctx) { |
| @@ -445,16 +458,13 @@ public: | |||
| 445 | 458 | ||
| 446 | const auto parameters{rp.PopRaw<Parameters>()}; | 459 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 447 | 460 | ||
| 448 | LOG_WARNING(Service_LDN, | 461 | LOG_INFO(Service_LDN, |
| 449 | "(STUBBED) called, passphrase_size={}, security_mode={}, " | 462 | "called, passphrase_size={}, security_mode={}, " |
| 450 | "local_communication_version={}", | 463 | "local_communication_version={}", |
| 451 | parameters.security_config.passphrase_size, | 464 | parameters.security_config.passphrase_size, |
| 452 | parameters.security_config.security_mode, | 465 | parameters.security_config.security_mode, parameters.local_communication_version); |
| 453 | parameters.local_communication_version); | ||
| 454 | 466 | ||
| 455 | const std::vector<u8> read_buffer = ctx.ReadBuffer(); | 467 | const std::vector<u8> read_buffer = ctx.ReadBuffer(); |
| 456 | NetworkInfo network_info{}; | ||
| 457 | |||
| 458 | if (read_buffer.size() != sizeof(NetworkInfo)) { | 468 | if (read_buffer.size() != sizeof(NetworkInfo)) { |
| 459 | LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!"); | 469 | LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!"); |
| 460 | IPC::ResponseBuilder rb{ctx, 2}; | 470 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -462,40 +472,47 @@ public: | |||
| 462 | return; | 472 | return; |
| 463 | } | 473 | } |
| 464 | 474 | ||
| 475 | NetworkInfo network_info{}; | ||
| 465 | std::memcpy(&network_info, read_buffer.data(), read_buffer.size()); | 476 | std::memcpy(&network_info, read_buffer.data(), read_buffer.size()); |
| 466 | 477 | ||
| 467 | IPC::ResponseBuilder rb{ctx, 2}; | 478 | IPC::ResponseBuilder rb{ctx, 2}; |
| 468 | rb.Push(ResultSuccess); | 479 | rb.Push(lan_discovery.Connect(network_info, parameters.user_config, |
| 480 | static_cast<u16>(parameters.local_communication_version))); | ||
| 469 | } | 481 | } |
| 470 | 482 | ||
| 471 | void Disconnect(Kernel::HLERequestContext& ctx) { | 483 | void Disconnect(Kernel::HLERequestContext& ctx) { |
| 472 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | 484 | LOG_INFO(Service_LDN, "called"); |
| 473 | 485 | ||
| 474 | IPC::ResponseBuilder rb{ctx, 2}; | 486 | IPC::ResponseBuilder rb{ctx, 2}; |
| 475 | rb.Push(ResultSuccess); | 487 | rb.Push(lan_discovery.Disconnect()); |
| 476 | } | 488 | } |
| 477 | void Initialize(Kernel::HLERequestContext& ctx) { | ||
| 478 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | ||
| 479 | 489 | ||
| 490 | void Initialize(Kernel::HLERequestContext& ctx) { | ||
| 480 | const auto rc = InitializeImpl(ctx); | 491 | const auto rc = InitializeImpl(ctx); |
| 492 | if (rc.IsError()) { | ||
| 493 | LOG_ERROR(Service_LDN, "Network isn't initialized, rc={}", rc.raw); | ||
| 494 | } | ||
| 481 | 495 | ||
| 482 | IPC::ResponseBuilder rb{ctx, 2}; | 496 | IPC::ResponseBuilder rb{ctx, 2}; |
| 483 | rb.Push(rc); | 497 | rb.Push(rc); |
| 484 | } | 498 | } |
| 485 | 499 | ||
| 486 | void Finalize(Kernel::HLERequestContext& ctx) { | 500 | void Finalize(Kernel::HLERequestContext& ctx) { |
| 487 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | 501 | if (auto room_member = room_network.GetRoomMember().lock()) { |
| 502 | room_member->Unbind(ldn_packet_received); | ||
| 503 | } | ||
| 488 | 504 | ||
| 489 | is_initialized = false; | 505 | is_initialized = false; |
| 490 | 506 | ||
| 491 | IPC::ResponseBuilder rb{ctx, 2}; | 507 | IPC::ResponseBuilder rb{ctx, 2}; |
| 492 | rb.Push(ResultSuccess); | 508 | rb.Push(lan_discovery.Finalize()); |
| 493 | } | 509 | } |
| 494 | 510 | ||
| 495 | void Initialize2(Kernel::HLERequestContext& ctx) { | 511 | void Initialize2(Kernel::HLERequestContext& ctx) { |
| 496 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | ||
| 497 | |||
| 498 | const auto rc = InitializeImpl(ctx); | 512 | const auto rc = InitializeImpl(ctx); |
| 513 | if (rc.IsError()) { | ||
| 514 | LOG_ERROR(Service_LDN, "Network isn't initialized, rc={}", rc.raw); | ||
| 515 | } | ||
| 499 | 516 | ||
| 500 | IPC::ResponseBuilder rb{ctx, 2}; | 517 | IPC::ResponseBuilder rb{ctx, 2}; |
| 501 | rb.Push(rc); | 518 | rb.Push(rc); |
| @@ -508,14 +525,26 @@ public: | |||
| 508 | return ResultAirplaneModeEnabled; | 525 | return ResultAirplaneModeEnabled; |
| 509 | } | 526 | } |
| 510 | 527 | ||
| 528 | if (auto room_member = room_network.GetRoomMember().lock()) { | ||
| 529 | ldn_packet_received = room_member->BindOnLdnPacketReceived( | ||
| 530 | [this](const Network::LDNPacket& packet) { OnLDNPacketReceived(packet); }); | ||
| 531 | } else { | ||
| 532 | LOG_ERROR(Service_LDN, "Couldn't bind callback!"); | ||
| 533 | return ResultAirplaneModeEnabled; | ||
| 534 | } | ||
| 535 | |||
| 536 | lan_discovery.Initialize([&]() { OnEventFired(); }); | ||
| 511 | is_initialized = true; | 537 | is_initialized = true; |
| 512 | // TODO (flTobi): Change this to ResultSuccess when LDN is fully implemented | 538 | return ResultSuccess; |
| 513 | return ResultAirplaneModeEnabled; | ||
| 514 | } | 539 | } |
| 515 | 540 | ||
| 516 | KernelHelpers::ServiceContext service_context; | 541 | KernelHelpers::ServiceContext service_context; |
| 517 | Kernel::KEvent* state_change_event; | 542 | Kernel::KEvent* state_change_event; |
| 518 | Network::RoomNetwork& room_network; | 543 | Network::RoomNetwork& room_network; |
| 544 | LANDiscovery lan_discovery; | ||
| 545 | |||
| 546 | // Callback identifier for the OnLDNPacketReceived event. | ||
| 547 | Network::RoomMember::CallbackHandle<Network::LDNPacket> ldn_packet_received; | ||
| 519 | 548 | ||
| 520 | bool is_initialized{}; | 549 | bool is_initialized{}; |
| 521 | }; | 550 | }; |
diff --git a/src/core/hle/service/ldn/ldn_types.h b/src/core/hle/service/ldn/ldn_types.h index 6231e936d..44c2c773b 100644 --- a/src/core/hle/service/ldn/ldn_types.h +++ b/src/core/hle/service/ldn/ldn_types.h | |||
| @@ -31,6 +31,8 @@ enum class NodeStateChange : u8 { | |||
| 31 | DisconnectAndConnect, | 31 | DisconnectAndConnect, |
| 32 | }; | 32 | }; |
| 33 | 33 | ||
| 34 | DECLARE_ENUM_FLAG_OPERATORS(NodeStateChange) | ||
| 35 | |||
| 34 | enum class ScanFilterFlag : u32 { | 36 | enum class ScanFilterFlag : u32 { |
| 35 | None = 0, | 37 | None = 0, |
| 36 | LocalCommunicationId = 1 << 0, | 38 | LocalCommunicationId = 1 << 0, |
| @@ -100,13 +102,13 @@ enum class AcceptPolicy : u8 { | |||
| 100 | 102 | ||
| 101 | enum class WifiChannel : s16 { | 103 | enum class WifiChannel : s16 { |
| 102 | Default = 0, | 104 | Default = 0, |
| 103 | wifi24_1 = 1, | 105 | Wifi24_1 = 1, |
| 104 | wifi24_6 = 6, | 106 | Wifi24_6 = 6, |
| 105 | wifi24_11 = 11, | 107 | Wifi24_11 = 11, |
| 106 | wifi50_36 = 36, | 108 | Wifi50_36 = 36, |
| 107 | wifi50_40 = 40, | 109 | Wifi50_40 = 40, |
| 108 | wifi50_44 = 44, | 110 | Wifi50_44 = 44, |
| 109 | wifi50_48 = 48, | 111 | Wifi50_48 = 48, |
| 110 | }; | 112 | }; |
| 111 | 113 | ||
| 112 | enum class LinkLevel : s8 { | 114 | enum class LinkLevel : s8 { |
| @@ -116,6 +118,11 @@ enum class LinkLevel : s8 { | |||
| 116 | Excellent, | 118 | Excellent, |
| 117 | }; | 119 | }; |
| 118 | 120 | ||
| 121 | enum class NodeStatus : u8 { | ||
| 122 | Disconnected, | ||
| 123 | Connected, | ||
| 124 | }; | ||
| 125 | |||
| 119 | struct NodeLatestUpdate { | 126 | struct NodeLatestUpdate { |
| 120 | NodeStateChange state_change; | 127 | NodeStateChange state_change; |
| 121 | INSERT_PADDING_BYTES(0x7); // Unknown | 128 | INSERT_PADDING_BYTES(0x7); // Unknown |
| @@ -150,7 +157,7 @@ struct Ssid { | |||
| 150 | 157 | ||
| 151 | Ssid() = default; | 158 | Ssid() = default; |
| 152 | 159 | ||
| 153 | explicit Ssid(std::string_view data) { | 160 | constexpr explicit Ssid(std::string_view data) { |
| 154 | length = static_cast<u8>(std::min(data.size(), SsidLengthMax)); | 161 | length = static_cast<u8>(std::min(data.size(), SsidLengthMax)); |
| 155 | data.copy(raw.data(), length); | 162 | data.copy(raw.data(), length); |
| 156 | raw[length] = 0; | 163 | raw[length] = 0; |
| @@ -159,19 +166,18 @@ struct Ssid { | |||
| 159 | std::string GetStringValue() const { | 166 | std::string GetStringValue() const { |
| 160 | return std::string(raw.data()); | 167 | return std::string(raw.data()); |
| 161 | } | 168 | } |
| 162 | }; | ||
| 163 | static_assert(sizeof(Ssid) == 0x22, "Ssid is an invalid size"); | ||
| 164 | 169 | ||
| 165 | struct Ipv4Address { | 170 | bool operator==(const Ssid& b) const { |
| 166 | union { | 171 | return (length == b.length) && (std::memcmp(raw.data(), b.raw.data(), length) == 0); |
| 167 | u32 raw{}; | 172 | } |
| 168 | std::array<u8, 4> bytes; | ||
| 169 | }; | ||
| 170 | 173 | ||
| 171 | std::string GetStringValue() const { | 174 | bool operator!=(const Ssid& b) const { |
| 172 | return fmt::format("{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]); | 175 | return !operator==(b); |
| 173 | } | 176 | } |
| 174 | }; | 177 | }; |
| 178 | static_assert(sizeof(Ssid) == 0x22, "Ssid is an invalid size"); | ||
| 179 | |||
| 180 | using Ipv4Address = std::array<u8, 4>; | ||
| 175 | static_assert(sizeof(Ipv4Address) == 0x4, "Ipv4Address is an invalid size"); | 181 | static_assert(sizeof(Ipv4Address) == 0x4, "Ipv4Address is an invalid size"); |
| 176 | 182 | ||
| 177 | struct MacAddress { | 183 | struct MacAddress { |
| @@ -181,6 +187,14 @@ struct MacAddress { | |||
| 181 | }; | 187 | }; |
| 182 | static_assert(sizeof(MacAddress) == 0x6, "MacAddress is an invalid size"); | 188 | static_assert(sizeof(MacAddress) == 0x6, "MacAddress is an invalid size"); |
| 183 | 189 | ||
| 190 | struct MACAddressHash { | ||
| 191 | size_t operator()(const MacAddress& address) const { | ||
| 192 | u64 value{}; | ||
| 193 | std::memcpy(&value, address.raw.data(), sizeof(address.raw)); | ||
| 194 | return value; | ||
| 195 | } | ||
| 196 | }; | ||
| 197 | |||
| 184 | struct ScanFilter { | 198 | struct ScanFilter { |
| 185 | NetworkId network_id; | 199 | NetworkId network_id; |
| 186 | NetworkType network_type; | 200 | NetworkType network_type; |
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 9b382bf56..93057e800 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include "core/hle/service/nvflinger/ui/graphic_buffer.h" | 22 | #include "core/hle/service/nvflinger/ui/graphic_buffer.h" |
| 23 | #include "core/hle/service/vi/display/vi_display.h" | 23 | #include "core/hle/service/vi/display/vi_display.h" |
| 24 | #include "core/hle/service/vi/layer/vi_layer.h" | 24 | #include "core/hle/service/vi/layer/vi_layer.h" |
| 25 | #include "core/hle/service/vi/vi_results.h" | ||
| 25 | #include "video_core/gpu.h" | 26 | #include "video_core/gpu.h" |
| 26 | 27 | ||
| 27 | namespace Service::NVFlinger { | 28 | namespace Service::NVFlinger { |
| @@ -163,15 +164,15 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) { | |||
| 163 | return layer->GetBinderId(); | 164 | return layer->GetBinderId(); |
| 164 | } | 165 | } |
| 165 | 166 | ||
| 166 | Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) { | 167 | ResultVal<Kernel::KReadableEvent*> NVFlinger::FindVsyncEvent(u64 display_id) { |
| 167 | const auto lock_guard = Lock(); | 168 | const auto lock_guard = Lock(); |
| 168 | auto* const display = FindDisplay(display_id); | 169 | auto* const display = FindDisplay(display_id); |
| 169 | 170 | ||
| 170 | if (display == nullptr) { | 171 | if (display == nullptr) { |
| 171 | return nullptr; | 172 | return VI::ResultNotFound; |
| 172 | } | 173 | } |
| 173 | 174 | ||
| 174 | return &display->GetVSyncEvent(); | 175 | return display->GetVSyncEvent(); |
| 175 | } | 176 | } |
| 176 | 177 | ||
| 177 | VI::Display* NVFlinger::FindDisplay(u64 display_id) { | 178 | VI::Display* NVFlinger::FindDisplay(u64 display_id) { |
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 044ac6ac8..3bbe5d92b 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include <vector> | 11 | #include <vector> |
| 12 | 12 | ||
| 13 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 14 | #include "core/hle/result.h" | ||
| 14 | #include "core/hle/service/kernel_helpers.h" | 15 | #include "core/hle/service/kernel_helpers.h" |
| 15 | 16 | ||
| 16 | namespace Common { | 17 | namespace Common { |
| @@ -71,8 +72,9 @@ public: | |||
| 71 | 72 | ||
| 72 | /// Gets the vsync event for the specified display. | 73 | /// Gets the vsync event for the specified display. |
| 73 | /// | 74 | /// |
| 74 | /// If an invalid display ID is provided, then nullptr is returned. | 75 | /// If an invalid display ID is provided, then VI::ResultNotFound is returned. |
| 75 | [[nodiscard]] Kernel::KReadableEvent* FindVsyncEvent(u64 display_id); | 76 | /// If the vsync event has already been retrieved, then VI::ResultPermissionDenied is returned. |
| 77 | [[nodiscard]] ResultVal<Kernel::KReadableEvent*> FindVsyncEvent(u64 display_id); | ||
| 76 | 78 | ||
| 77 | /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when | 79 | /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when |
| 78 | /// finished. | 80 | /// finished. |
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp index b34febb50..aa49aa775 100644 --- a/src/core/hle/service/vi/display/vi_display.cpp +++ b/src/core/hle/service/vi/display/vi_display.cpp | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include "core/hle/service/nvflinger/hos_binder_driver_server.h" | 19 | #include "core/hle/service/nvflinger/hos_binder_driver_server.h" |
| 20 | #include "core/hle/service/vi/display/vi_display.h" | 20 | #include "core/hle/service/vi/display/vi_display.h" |
| 21 | #include "core/hle/service/vi/layer/vi_layer.h" | 21 | #include "core/hle/service/vi/layer/vi_layer.h" |
| 22 | #include "core/hle/service/vi/vi_results.h" | ||
| 22 | 23 | ||
| 23 | namespace Service::VI { | 24 | namespace Service::VI { |
| 24 | 25 | ||
| @@ -55,8 +56,18 @@ const Layer& Display::GetLayer(std::size_t index) const { | |||
| 55 | return *layers.at(index); | 56 | return *layers.at(index); |
| 56 | } | 57 | } |
| 57 | 58 | ||
| 58 | Kernel::KReadableEvent& Display::GetVSyncEvent() { | 59 | ResultVal<Kernel::KReadableEvent*> Display::GetVSyncEvent() { |
| 59 | return vsync_event->GetReadableEvent(); | 60 | if (got_vsync_event) { |
| 61 | return ResultPermissionDenied; | ||
| 62 | } | ||
| 63 | |||
| 64 | got_vsync_event = true; | ||
| 65 | |||
| 66 | return GetVSyncEventUnchecked(); | ||
| 67 | } | ||
| 68 | |||
| 69 | Kernel::KReadableEvent* Display::GetVSyncEventUnchecked() { | ||
| 70 | return &vsync_event->GetReadableEvent(); | ||
| 60 | } | 71 | } |
| 61 | 72 | ||
| 62 | void Display::SignalVSyncEvent() { | 73 | void Display::SignalVSyncEvent() { |
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h index 3838bb599..8dbb0ef80 100644 --- a/src/core/hle/service/vi/display/vi_display.h +++ b/src/core/hle/service/vi/display/vi_display.h | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | 9 | ||
| 10 | #include "common/common_funcs.h" | 10 | #include "common/common_funcs.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "core/hle/result.h" | ||
| 12 | 13 | ||
| 13 | namespace Kernel { | 14 | namespace Kernel { |
| 14 | class KEvent; | 15 | class KEvent; |
| @@ -73,8 +74,16 @@ public: | |||
| 73 | return layers.size(); | 74 | return layers.size(); |
| 74 | } | 75 | } |
| 75 | 76 | ||
| 76 | /// Gets the readable vsync event. | 77 | /** |
| 77 | Kernel::KReadableEvent& GetVSyncEvent(); | 78 | * Gets the internal vsync event. |
| 79 | * | ||
| 80 | * @returns The internal Vsync event if it has not yet been retrieved, | ||
| 81 | * VI::ResultPermissionDenied otherwise. | ||
| 82 | */ | ||
| 83 | [[nodiscard]] ResultVal<Kernel::KReadableEvent*> GetVSyncEvent(); | ||
| 84 | |||
| 85 | /// Gets the internal vsync event. | ||
| 86 | Kernel::KReadableEvent* GetVSyncEventUnchecked(); | ||
| 78 | 87 | ||
| 79 | /// Signals the internal vsync event. | 88 | /// Signals the internal vsync event. |
| 80 | void SignalVSyncEvent(); | 89 | void SignalVSyncEvent(); |
| @@ -118,6 +127,7 @@ private: | |||
| 118 | 127 | ||
| 119 | std::vector<std::unique_ptr<Layer>> layers; | 128 | std::vector<std::unique_ptr<Layer>> layers; |
| 120 | Kernel::KEvent* vsync_event{}; | 129 | Kernel::KEvent* vsync_event{}; |
| 130 | bool got_vsync_event{false}; | ||
| 121 | }; | 131 | }; |
| 122 | 132 | ||
| 123 | } // namespace Service::VI | 133 | } // namespace Service::VI |
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 546879648..f083811ec 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp | |||
| @@ -29,16 +29,12 @@ | |||
| 29 | #include "core/hle/service/service.h" | 29 | #include "core/hle/service/service.h" |
| 30 | #include "core/hle/service/vi/vi.h" | 30 | #include "core/hle/service/vi/vi.h" |
| 31 | #include "core/hle/service/vi/vi_m.h" | 31 | #include "core/hle/service/vi/vi_m.h" |
| 32 | #include "core/hle/service/vi/vi_results.h" | ||
| 32 | #include "core/hle/service/vi/vi_s.h" | 33 | #include "core/hle/service/vi/vi_s.h" |
| 33 | #include "core/hle/service/vi/vi_u.h" | 34 | #include "core/hle/service/vi/vi_u.h" |
| 34 | 35 | ||
| 35 | namespace Service::VI { | 36 | namespace Service::VI { |
| 36 | 37 | ||
| 37 | constexpr Result ERR_OPERATION_FAILED{ErrorModule::VI, 1}; | ||
| 38 | constexpr Result ERR_PERMISSION_DENIED{ErrorModule::VI, 5}; | ||
| 39 | constexpr Result ERR_UNSUPPORTED{ErrorModule::VI, 6}; | ||
| 40 | constexpr Result ERR_NOT_FOUND{ErrorModule::VI, 7}; | ||
| 41 | |||
| 42 | struct DisplayInfo { | 38 | struct DisplayInfo { |
| 43 | /// The name of this particular display. | 39 | /// The name of this particular display. |
| 44 | char display_name[0x40]{"Default"}; | 40 | char display_name[0x40]{"Default"}; |
| @@ -348,7 +344,7 @@ private: | |||
| 348 | if (!layer_id) { | 344 | if (!layer_id) { |
| 349 | LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display); | 345 | LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display); |
| 350 | IPC::ResponseBuilder rb{ctx, 2}; | 346 | IPC::ResponseBuilder rb{ctx, 2}; |
| 351 | rb.Push(ERR_NOT_FOUND); | 347 | rb.Push(ResultNotFound); |
| 352 | return; | 348 | return; |
| 353 | } | 349 | } |
| 354 | 350 | ||
| @@ -498,7 +494,7 @@ private: | |||
| 498 | if (!display_id) { | 494 | if (!display_id) { |
| 499 | LOG_ERROR(Service_VI, "Display not found! display_name={}", name); | 495 | LOG_ERROR(Service_VI, "Display not found! display_name={}", name); |
| 500 | IPC::ResponseBuilder rb{ctx, 2}; | 496 | IPC::ResponseBuilder rb{ctx, 2}; |
| 501 | rb.Push(ERR_NOT_FOUND); | 497 | rb.Push(ResultNotFound); |
| 502 | return; | 498 | return; |
| 503 | } | 499 | } |
| 504 | 500 | ||
| @@ -554,14 +550,14 @@ private: | |||
| 554 | 550 | ||
| 555 | if (scaling_mode > NintendoScaleMode::PreserveAspectRatio) { | 551 | if (scaling_mode > NintendoScaleMode::PreserveAspectRatio) { |
| 556 | LOG_ERROR(Service_VI, "Invalid scaling mode provided."); | 552 | LOG_ERROR(Service_VI, "Invalid scaling mode provided."); |
| 557 | rb.Push(ERR_OPERATION_FAILED); | 553 | rb.Push(ResultOperationFailed); |
| 558 | return; | 554 | return; |
| 559 | } | 555 | } |
| 560 | 556 | ||
| 561 | if (scaling_mode != NintendoScaleMode::ScaleToWindow && | 557 | if (scaling_mode != NintendoScaleMode::ScaleToWindow && |
| 562 | scaling_mode != NintendoScaleMode::PreserveAspectRatio) { | 558 | scaling_mode != NintendoScaleMode::PreserveAspectRatio) { |
| 563 | LOG_ERROR(Service_VI, "Unsupported scaling mode supplied."); | 559 | LOG_ERROR(Service_VI, "Unsupported scaling mode supplied."); |
| 564 | rb.Push(ERR_UNSUPPORTED); | 560 | rb.Push(ResultNotSupported); |
| 565 | return; | 561 | return; |
| 566 | } | 562 | } |
| 567 | 563 | ||
| @@ -594,7 +590,7 @@ private: | |||
| 594 | if (!display_id) { | 590 | if (!display_id) { |
| 595 | LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id); | 591 | LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id); |
| 596 | IPC::ResponseBuilder rb{ctx, 2}; | 592 | IPC::ResponseBuilder rb{ctx, 2}; |
| 597 | rb.Push(ERR_NOT_FOUND); | 593 | rb.Push(ResultNotFound); |
| 598 | return; | 594 | return; |
| 599 | } | 595 | } |
| 600 | 596 | ||
| @@ -602,7 +598,7 @@ private: | |||
| 602 | if (!buffer_queue_id) { | 598 | if (!buffer_queue_id) { |
| 603 | LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id); | 599 | LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id); |
| 604 | IPC::ResponseBuilder rb{ctx, 2}; | 600 | IPC::ResponseBuilder rb{ctx, 2}; |
| 605 | rb.Push(ERR_NOT_FOUND); | 601 | rb.Push(ResultNotFound); |
| 606 | return; | 602 | return; |
| 607 | } | 603 | } |
| 608 | 604 | ||
| @@ -640,7 +636,7 @@ private: | |||
| 640 | if (!layer_id) { | 636 | if (!layer_id) { |
| 641 | LOG_ERROR(Service_VI, "Layer not found! display_id={}", display_id); | 637 | LOG_ERROR(Service_VI, "Layer not found! display_id={}", display_id); |
| 642 | IPC::ResponseBuilder rb{ctx, 2}; | 638 | IPC::ResponseBuilder rb{ctx, 2}; |
| 643 | rb.Push(ERR_NOT_FOUND); | 639 | rb.Push(ResultNotFound); |
| 644 | return; | 640 | return; |
| 645 | } | 641 | } |
| 646 | 642 | ||
| @@ -648,7 +644,7 @@ private: | |||
| 648 | if (!buffer_queue_id) { | 644 | if (!buffer_queue_id) { |
| 649 | LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id); | 645 | LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id); |
| 650 | IPC::ResponseBuilder rb{ctx, 2}; | 646 | IPC::ResponseBuilder rb{ctx, 2}; |
| 651 | rb.Push(ERR_NOT_FOUND); | 647 | rb.Push(ResultNotFound); |
| 652 | return; | 648 | return; |
| 653 | } | 649 | } |
| 654 | 650 | ||
| @@ -675,19 +671,23 @@ private: | |||
| 675 | IPC::RequestParser rp{ctx}; | 671 | IPC::RequestParser rp{ctx}; |
| 676 | const u64 display_id = rp.Pop<u64>(); | 672 | const u64 display_id = rp.Pop<u64>(); |
| 677 | 673 | ||
| 678 | LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id); | 674 | LOG_DEBUG(Service_VI, "called. display_id={}", display_id); |
| 679 | 675 | ||
| 680 | const auto vsync_event = nv_flinger.FindVsyncEvent(display_id); | 676 | const auto vsync_event = nv_flinger.FindVsyncEvent(display_id); |
| 681 | if (!vsync_event) { | 677 | if (vsync_event.Failed()) { |
| 682 | LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id); | 678 | const auto result = vsync_event.Code(); |
| 679 | if (result == ResultNotFound) { | ||
| 680 | LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id); | ||
| 681 | } | ||
| 682 | |||
| 683 | IPC::ResponseBuilder rb{ctx, 2}; | 683 | IPC::ResponseBuilder rb{ctx, 2}; |
| 684 | rb.Push(ERR_NOT_FOUND); | 684 | rb.Push(result); |
| 685 | return; | 685 | return; |
| 686 | } | 686 | } |
| 687 | 687 | ||
| 688 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 688 | IPC::ResponseBuilder rb{ctx, 2, 1}; |
| 689 | rb.Push(ResultSuccess); | 689 | rb.Push(ResultSuccess); |
| 690 | rb.PushCopyObjects(vsync_event); | 690 | rb.PushCopyObjects(*vsync_event); |
| 691 | } | 691 | } |
| 692 | 692 | ||
| 693 | void ConvertScalingMode(Kernel::HLERequestContext& ctx) { | 693 | void ConvertScalingMode(Kernel::HLERequestContext& ctx) { |
| @@ -764,7 +764,7 @@ private: | |||
| 764 | return ConvertedScaleMode::PreserveAspectRatio; | 764 | return ConvertedScaleMode::PreserveAspectRatio; |
| 765 | default: | 765 | default: |
| 766 | LOG_ERROR(Service_VI, "Invalid scaling mode specified, mode={}", mode); | 766 | LOG_ERROR(Service_VI, "Invalid scaling mode specified, mode={}", mode); |
| 767 | return ERR_OPERATION_FAILED; | 767 | return ResultOperationFailed; |
| 768 | } | 768 | } |
| 769 | } | 769 | } |
| 770 | 770 | ||
| @@ -794,7 +794,7 @@ void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& | |||
| 794 | if (!IsValidServiceAccess(permission, policy)) { | 794 | if (!IsValidServiceAccess(permission, policy)) { |
| 795 | LOG_ERROR(Service_VI, "Permission denied for policy {}", policy); | 795 | LOG_ERROR(Service_VI, "Permission denied for policy {}", policy); |
| 796 | IPC::ResponseBuilder rb{ctx, 2}; | 796 | IPC::ResponseBuilder rb{ctx, 2}; |
| 797 | rb.Push(ERR_PERMISSION_DENIED); | 797 | rb.Push(ResultPermissionDenied); |
| 798 | return; | 798 | return; |
| 799 | } | 799 | } |
| 800 | 800 | ||
diff --git a/src/core/hle/service/vi/vi_results.h b/src/core/hle/service/vi/vi_results.h new file mode 100644 index 000000000..a46c247d2 --- /dev/null +++ b/src/core/hle/service/vi/vi_results.h | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/result.h" | ||
| 5 | |||
| 6 | namespace Service::VI { | ||
| 7 | |||
| 8 | constexpr Result ResultOperationFailed{ErrorModule::VI, 1}; | ||
| 9 | constexpr Result ResultPermissionDenied{ErrorModule::VI, 5}; | ||
| 10 | constexpr Result ResultNotSupported{ErrorModule::VI, 6}; | ||
| 11 | constexpr Result ResultNotFound{ErrorModule::VI, 7}; | ||
| 12 | |||
| 13 | } // namespace Service::VI | ||
diff --git a/src/core/internal_network/network_interface.cpp b/src/core/internal_network/network_interface.cpp index 0f0a66160..057fd3661 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() { | |||
| 188 | std::optional<NetworkInterface> GetSelectedNetworkInterface() { | 188 | std::optional<NetworkInterface> GetSelectedNetworkInterface() { |
| 189 | const auto& selected_network_interface = Settings::values.network_interface.GetValue(); | 189 | const auto& selected_network_interface = Settings::values.network_interface.GetValue(); |
| 190 | const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); | 190 | const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); |
| 191 | if (network_interfaces.size() == 0) { | 191 | if (network_interfaces.empty()) { |
| 192 | LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); | 192 | LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); |
| 193 | return std::nullopt; | 193 | return std::nullopt; |
| 194 | } | 194 | } |
| @@ -206,4 +206,14 @@ std::optional<NetworkInterface> GetSelectedNetworkInterface() { | |||
| 206 | return *res; | 206 | return *res; |
| 207 | } | 207 | } |
| 208 | 208 | ||
| 209 | void SelectFirstNetworkInterface() { | ||
| 210 | const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); | ||
| 211 | |||
| 212 | if (network_interfaces.empty()) { | ||
| 213 | return; | ||
| 214 | } | ||
| 215 | |||
| 216 | Settings::values.network_interface.SetValue(network_interfaces[0].name); | ||
| 217 | } | ||
| 218 | |||
| 209 | } // namespace Network | 219 | } // namespace Network |
diff --git a/src/core/internal_network/network_interface.h b/src/core/internal_network/network_interface.h index 9b98b6b42..175e61b1f 100644 --- a/src/core/internal_network/network_interface.h +++ b/src/core/internal_network/network_interface.h | |||
| @@ -24,5 +24,6 @@ struct NetworkInterface { | |||
| 24 | 24 | ||
| 25 | std::vector<NetworkInterface> GetAvailableNetworkInterfaces(); | 25 | std::vector<NetworkInterface> GetAvailableNetworkInterfaces(); |
| 26 | std::optional<NetworkInterface> GetSelectedNetworkInterface(); | 26 | std::optional<NetworkInterface> GetSelectedNetworkInterface(); |
| 27 | void SelectFirstNetworkInterface(); | ||
| 27 | 28 | ||
| 28 | } // namespace Network | 29 | } // namespace Network |
diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp index 0c746bd82..7d5d37bbc 100644 --- a/src/core/internal_network/socket_proxy.cpp +++ b/src/core/internal_network/socket_proxy.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "common/zstd_compression.h" | ||
| 9 | #include "core/internal_network/network.h" | 10 | #include "core/internal_network/network.h" |
| 10 | #include "core/internal_network/network_interface.h" | 11 | #include "core/internal_network/network_interface.h" |
| 11 | #include "core/internal_network/socket_proxy.h" | 12 | #include "core/internal_network/socket_proxy.h" |
| @@ -32,8 +33,11 @@ void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) { | |||
| 32 | return; | 33 | return; |
| 33 | } | 34 | } |
| 34 | 35 | ||
| 36 | auto decompressed = packet; | ||
| 37 | decompressed.data = Common::Compression::DecompressDataZSTD(packet.data); | ||
| 38 | |||
| 35 | std::lock_guard guard(packets_mutex); | 39 | std::lock_guard guard(packets_mutex); |
| 36 | received_packets.push(packet); | 40 | received_packets.push(decompressed); |
| 37 | } | 41 | } |
| 38 | 42 | ||
| 39 | template <typename T> | 43 | template <typename T> |
| @@ -185,6 +189,8 @@ std::pair<s32, Errno> ProxySocket::Send(const std::vector<u8>& message, int flag | |||
| 185 | void ProxySocket::SendPacket(ProxyPacket& packet) { | 189 | void ProxySocket::SendPacket(ProxyPacket& packet) { |
| 186 | if (auto room_member = room_network.GetRoomMember().lock()) { | 190 | if (auto room_member = room_network.GetRoomMember().lock()) { |
| 187 | if (room_member->IsConnected()) { | 191 | if (room_member->IsConnected()) { |
| 192 | packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(), | ||
| 193 | packet.data.size()); | ||
| 188 | room_member->SendProxyPacket(packet); | 194 | room_member->SendProxyPacket(packet); |
| 189 | } | 195 | } |
| 190 | } | 196 | } |
diff --git a/src/dedicated_room/yuzu_room.cpp b/src/dedicated_room/yuzu_room.cpp index 7b6deba41..359891883 100644 --- a/src/dedicated_room/yuzu_room.cpp +++ b/src/dedicated_room/yuzu_room.cpp | |||
| @@ -76,7 +76,18 @@ static constexpr char BanListMagic[] = "YuzuRoom-BanList-1"; | |||
| 76 | static constexpr char token_delimiter{':'}; | 76 | static constexpr char token_delimiter{':'}; |
| 77 | 77 | ||
| 78 | static void PadToken(std::string& token) { | 78 | static void PadToken(std::string& token) { |
| 79 | while (token.size() % 4 != 0) { | 79 | std::size_t outlen = 0; |
| 80 | |||
| 81 | std::array<unsigned char, 512> output{}; | ||
| 82 | std::array<unsigned char, 2048> roundtrip{}; | ||
| 83 | for (size_t i = 0; i < 3; i++) { | ||
| 84 | mbedtls_base64_decode(output.data(), output.size(), &outlen, | ||
| 85 | reinterpret_cast<const unsigned char*>(token.c_str()), | ||
| 86 | token.length()); | ||
| 87 | mbedtls_base64_encode(roundtrip.data(), roundtrip.size(), &outlen, output.data(), outlen); | ||
| 88 | if (memcmp(roundtrip.data(), token.data(), token.size()) == 0) { | ||
| 89 | break; | ||
| 90 | } | ||
| 80 | token.push_back('='); | 91 | token.push_back('='); |
| 81 | } | 92 | } |
| 82 | } | 93 | } |
diff --git a/src/network/room.cpp b/src/network/room.cpp index 8c63b255b..dc5dbce7f 100644 --- a/src/network/room.cpp +++ b/src/network/room.cpp | |||
| @@ -212,6 +212,12 @@ public: | |||
| 212 | void HandleProxyPacket(const ENetEvent* event); | 212 | void HandleProxyPacket(const ENetEvent* event); |
| 213 | 213 | ||
| 214 | /** | 214 | /** |
| 215 | * Broadcasts this packet to all members except the sender. | ||
| 216 | * @param event The ENet event containing the data | ||
| 217 | */ | ||
| 218 | void HandleLdnPacket(const ENetEvent* event); | ||
| 219 | |||
| 220 | /** | ||
| 215 | * Extracts a chat entry from a received ENet packet and adds it to the chat queue. | 221 | * Extracts a chat entry from a received ENet packet and adds it to the chat queue. |
| 216 | * @param event The ENet event that was received. | 222 | * @param event The ENet event that was received. |
| 217 | */ | 223 | */ |
| @@ -247,6 +253,9 @@ void Room::RoomImpl::ServerLoop() { | |||
| 247 | case IdProxyPacket: | 253 | case IdProxyPacket: |
| 248 | HandleProxyPacket(&event); | 254 | HandleProxyPacket(&event); |
| 249 | break; | 255 | break; |
| 256 | case IdLdnPacket: | ||
| 257 | HandleLdnPacket(&event); | ||
| 258 | break; | ||
| 250 | case IdChatMessage: | 259 | case IdChatMessage: |
| 251 | HandleChatPacket(&event); | 260 | HandleChatPacket(&event); |
| 252 | break; | 261 | break; |
| @@ -861,6 +870,60 @@ void Room::RoomImpl::HandleProxyPacket(const ENetEvent* event) { | |||
| 861 | enet_host_flush(server); | 870 | enet_host_flush(server); |
| 862 | } | 871 | } |
| 863 | 872 | ||
| 873 | void Room::RoomImpl::HandleLdnPacket(const ENetEvent* event) { | ||
| 874 | Packet in_packet; | ||
| 875 | in_packet.Append(event->packet->data, event->packet->dataLength); | ||
| 876 | |||
| 877 | in_packet.IgnoreBytes(sizeof(u8)); // Message type | ||
| 878 | |||
| 879 | in_packet.IgnoreBytes(sizeof(u8)); // LAN packet type | ||
| 880 | in_packet.IgnoreBytes(sizeof(IPv4Address)); // Local IP | ||
| 881 | |||
| 882 | IPv4Address remote_ip; | ||
| 883 | in_packet.Read(remote_ip); // Remote IP | ||
| 884 | |||
| 885 | bool broadcast; | ||
| 886 | in_packet.Read(broadcast); // Broadcast | ||
| 887 | |||
| 888 | Packet out_packet; | ||
| 889 | out_packet.Append(event->packet->data, event->packet->dataLength); | ||
| 890 | ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(), | ||
| 891 | ENET_PACKET_FLAG_RELIABLE); | ||
| 892 | |||
| 893 | const auto& destination_address = remote_ip; | ||
| 894 | if (broadcast) { // Send the data to everyone except the sender | ||
| 895 | std::lock_guard lock(member_mutex); | ||
| 896 | bool sent_packet = false; | ||
| 897 | for (const auto& member : members) { | ||
| 898 | if (member.peer != event->peer) { | ||
| 899 | sent_packet = true; | ||
| 900 | enet_peer_send(member.peer, 0, enet_packet); | ||
| 901 | } | ||
| 902 | } | ||
| 903 | |||
| 904 | if (!sent_packet) { | ||
| 905 | enet_packet_destroy(enet_packet); | ||
| 906 | } | ||
| 907 | } else { | ||
| 908 | std::lock_guard lock(member_mutex); | ||
| 909 | auto member = std::find_if(members.begin(), members.end(), | ||
| 910 | [destination_address](const Member& member_entry) -> bool { | ||
| 911 | return member_entry.fake_ip == destination_address; | ||
| 912 | }); | ||
| 913 | if (member != members.end()) { | ||
| 914 | enet_peer_send(member->peer, 0, enet_packet); | ||
| 915 | } else { | ||
| 916 | LOG_ERROR(Network, | ||
| 917 | "Attempting to send to unknown IP address: " | ||
| 918 | "{}.{}.{}.{}", | ||
| 919 | destination_address[0], destination_address[1], destination_address[2], | ||
| 920 | destination_address[3]); | ||
| 921 | enet_packet_destroy(enet_packet); | ||
| 922 | } | ||
| 923 | } | ||
| 924 | enet_host_flush(server); | ||
| 925 | } | ||
| 926 | |||
| 864 | void Room::RoomImpl::HandleChatPacket(const ENetEvent* event) { | 927 | void Room::RoomImpl::HandleChatPacket(const ENetEvent* event) { |
| 865 | Packet in_packet; | 928 | Packet in_packet; |
| 866 | in_packet.Append(event->packet->data, event->packet->dataLength); | 929 | in_packet.Append(event->packet->data, event->packet->dataLength); |
diff --git a/src/network/room.h b/src/network/room.h index c2a4b1a70..edbd3ecfb 100644 --- a/src/network/room.h +++ b/src/network/room.h | |||
| @@ -40,6 +40,7 @@ enum RoomMessageTypes : u8 { | |||
| 40 | IdRoomInformation, | 40 | IdRoomInformation, |
| 41 | IdSetGameInfo, | 41 | IdSetGameInfo, |
| 42 | IdProxyPacket, | 42 | IdProxyPacket, |
| 43 | IdLdnPacket, | ||
| 43 | IdChatMessage, | 44 | IdChatMessage, |
| 44 | IdNameCollision, | 45 | IdNameCollision, |
| 45 | IdIpCollision, | 46 | IdIpCollision, |
diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp index 06818af78..b94cb24ad 100644 --- a/src/network/room_member.cpp +++ b/src/network/room_member.cpp | |||
| @@ -58,6 +58,7 @@ public: | |||
| 58 | 58 | ||
| 59 | private: | 59 | private: |
| 60 | CallbackSet<ProxyPacket> callback_set_proxy_packet; | 60 | CallbackSet<ProxyPacket> callback_set_proxy_packet; |
| 61 | CallbackSet<LDNPacket> callback_set_ldn_packet; | ||
| 61 | CallbackSet<ChatEntry> callback_set_chat_messages; | 62 | CallbackSet<ChatEntry> callback_set_chat_messages; |
| 62 | CallbackSet<StatusMessageEntry> callback_set_status_messages; | 63 | CallbackSet<StatusMessageEntry> callback_set_status_messages; |
| 63 | CallbackSet<RoomInformation> callback_set_room_information; | 64 | CallbackSet<RoomInformation> callback_set_room_information; |
| @@ -108,6 +109,12 @@ public: | |||
| 108 | void HandleProxyPackets(const ENetEvent* event); | 109 | void HandleProxyPackets(const ENetEvent* event); |
| 109 | 110 | ||
| 110 | /** | 111 | /** |
| 112 | * Extracts an LdnPacket from a received ENet packet. | ||
| 113 | * @param event The ENet event that was received. | ||
| 114 | */ | ||
| 115 | void HandleLdnPackets(const ENetEvent* event); | ||
| 116 | |||
| 117 | /** | ||
| 111 | * Extracts a chat entry from a received ENet packet and adds it to the chat queue. | 118 | * Extracts a chat entry from a received ENet packet and adds it to the chat queue. |
| 112 | * @param event The ENet event that was received. | 119 | * @param event The ENet event that was received. |
| 113 | */ | 120 | */ |
| @@ -166,6 +173,9 @@ void RoomMember::RoomMemberImpl::MemberLoop() { | |||
| 166 | case IdProxyPacket: | 173 | case IdProxyPacket: |
| 167 | HandleProxyPackets(&event); | 174 | HandleProxyPackets(&event); |
| 168 | break; | 175 | break; |
| 176 | case IdLdnPacket: | ||
| 177 | HandleLdnPackets(&event); | ||
| 178 | break; | ||
| 169 | case IdChatMessage: | 179 | case IdChatMessage: |
| 170 | HandleChatPacket(&event); | 180 | HandleChatPacket(&event); |
| 171 | break; | 181 | break; |
| @@ -372,6 +382,27 @@ void RoomMember::RoomMemberImpl::HandleProxyPackets(const ENetEvent* event) { | |||
| 372 | Invoke<ProxyPacket>(proxy_packet); | 382 | Invoke<ProxyPacket>(proxy_packet); |
| 373 | } | 383 | } |
| 374 | 384 | ||
| 385 | void RoomMember::RoomMemberImpl::HandleLdnPackets(const ENetEvent* event) { | ||
| 386 | LDNPacket ldn_packet{}; | ||
| 387 | Packet packet; | ||
| 388 | packet.Append(event->packet->data, event->packet->dataLength); | ||
| 389 | |||
| 390 | // Ignore the first byte, which is the message id. | ||
| 391 | packet.IgnoreBytes(sizeof(u8)); // Ignore the message type | ||
| 392 | |||
| 393 | u8 packet_type; | ||
| 394 | packet.Read(packet_type); | ||
| 395 | ldn_packet.type = static_cast<LDNPacketType>(packet_type); | ||
| 396 | |||
| 397 | packet.Read(ldn_packet.local_ip); | ||
| 398 | packet.Read(ldn_packet.remote_ip); | ||
| 399 | packet.Read(ldn_packet.broadcast); | ||
| 400 | |||
| 401 | packet.Read(ldn_packet.data); | ||
| 402 | |||
| 403 | Invoke<LDNPacket>(ldn_packet); | ||
| 404 | } | ||
| 405 | |||
| 375 | void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) { | 406 | void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) { |
| 376 | Packet packet; | 407 | Packet packet; |
| 377 | packet.Append(event->packet->data, event->packet->dataLength); | 408 | packet.Append(event->packet->data, event->packet->dataLength); |
| @@ -450,6 +481,11 @@ RoomMember::RoomMemberImpl::CallbackSet<ProxyPacket>& RoomMember::RoomMemberImpl | |||
| 450 | } | 481 | } |
| 451 | 482 | ||
| 452 | template <> | 483 | template <> |
| 484 | RoomMember::RoomMemberImpl::CallbackSet<LDNPacket>& RoomMember::RoomMemberImpl::Callbacks::Get() { | ||
| 485 | return callback_set_ldn_packet; | ||
| 486 | } | ||
| 487 | |||
| 488 | template <> | ||
| 453 | RoomMember::RoomMemberImpl::CallbackSet<RoomMember::State>& | 489 | RoomMember::RoomMemberImpl::CallbackSet<RoomMember::State>& |
| 454 | RoomMember::RoomMemberImpl::Callbacks::Get() { | 490 | RoomMember::RoomMemberImpl::Callbacks::Get() { |
| 455 | return callback_set_state; | 491 | return callback_set_state; |
| @@ -607,6 +643,21 @@ void RoomMember::SendProxyPacket(const ProxyPacket& proxy_packet) { | |||
| 607 | room_member_impl->Send(std::move(packet)); | 643 | room_member_impl->Send(std::move(packet)); |
| 608 | } | 644 | } |
| 609 | 645 | ||
| 646 | void RoomMember::SendLdnPacket(const LDNPacket& ldn_packet) { | ||
| 647 | Packet packet; | ||
| 648 | packet.Write(static_cast<u8>(IdLdnPacket)); | ||
| 649 | |||
| 650 | packet.Write(static_cast<u8>(ldn_packet.type)); | ||
| 651 | |||
| 652 | packet.Write(ldn_packet.local_ip); | ||
| 653 | packet.Write(ldn_packet.remote_ip); | ||
| 654 | packet.Write(ldn_packet.broadcast); | ||
| 655 | |||
| 656 | packet.Write(ldn_packet.data); | ||
| 657 | |||
| 658 | room_member_impl->Send(std::move(packet)); | ||
| 659 | } | ||
| 660 | |||
| 610 | void RoomMember::SendChatMessage(const std::string& message) { | 661 | void RoomMember::SendChatMessage(const std::string& message) { |
| 611 | Packet packet; | 662 | Packet packet; |
| 612 | packet.Write(static_cast<u8>(IdChatMessage)); | 663 | packet.Write(static_cast<u8>(IdChatMessage)); |
| @@ -663,6 +714,11 @@ RoomMember::CallbackHandle<ProxyPacket> RoomMember::BindOnProxyPacketReceived( | |||
| 663 | return room_member_impl->Bind(callback); | 714 | return room_member_impl->Bind(callback); |
| 664 | } | 715 | } |
| 665 | 716 | ||
| 717 | RoomMember::CallbackHandle<LDNPacket> RoomMember::BindOnLdnPacketReceived( | ||
| 718 | std::function<void(const LDNPacket&)> callback) { | ||
| 719 | return room_member_impl->Bind(std::move(callback)); | ||
| 720 | } | ||
| 721 | |||
| 666 | RoomMember::CallbackHandle<RoomInformation> RoomMember::BindOnRoomInformationChanged( | 722 | RoomMember::CallbackHandle<RoomInformation> RoomMember::BindOnRoomInformationChanged( |
| 667 | std::function<void(const RoomInformation&)> callback) { | 723 | std::function<void(const RoomInformation&)> callback) { |
| 668 | return room_member_impl->Bind(callback); | 724 | return room_member_impl->Bind(callback); |
| @@ -699,6 +755,7 @@ void RoomMember::Leave() { | |||
| 699 | } | 755 | } |
| 700 | 756 | ||
| 701 | template void RoomMember::Unbind(CallbackHandle<ProxyPacket>); | 757 | template void RoomMember::Unbind(CallbackHandle<ProxyPacket>); |
| 758 | template void RoomMember::Unbind(CallbackHandle<LDNPacket>); | ||
| 702 | template void RoomMember::Unbind(CallbackHandle<RoomMember::State>); | 759 | template void RoomMember::Unbind(CallbackHandle<RoomMember::State>); |
| 703 | template void RoomMember::Unbind(CallbackHandle<RoomMember::Error>); | 760 | template void RoomMember::Unbind(CallbackHandle<RoomMember::Error>); |
| 704 | template void RoomMember::Unbind(CallbackHandle<RoomInformation>); | 761 | template void RoomMember::Unbind(CallbackHandle<RoomInformation>); |
diff --git a/src/network/room_member.h b/src/network/room_member.h index f578f7f6a..0d6417294 100644 --- a/src/network/room_member.h +++ b/src/network/room_member.h | |||
| @@ -17,7 +17,24 @@ namespace Network { | |||
| 17 | using AnnounceMultiplayerRoom::GameInfo; | 17 | using AnnounceMultiplayerRoom::GameInfo; |
| 18 | using AnnounceMultiplayerRoom::RoomInformation; | 18 | using AnnounceMultiplayerRoom::RoomInformation; |
| 19 | 19 | ||
| 20 | /// Information about the received WiFi packets. | 20 | enum class LDNPacketType : u8 { |
| 21 | Scan, | ||
| 22 | ScanResp, | ||
| 23 | Connect, | ||
| 24 | SyncNetwork, | ||
| 25 | Disconnect, | ||
| 26 | DestroyNetwork, | ||
| 27 | }; | ||
| 28 | |||
| 29 | struct LDNPacket { | ||
| 30 | LDNPacketType type; | ||
| 31 | IPv4Address local_ip; | ||
| 32 | IPv4Address remote_ip; | ||
| 33 | bool broadcast; | ||
| 34 | std::vector<u8> data; | ||
| 35 | }; | ||
| 36 | |||
| 37 | /// Information about the received proxy packets. | ||
| 21 | struct ProxyPacket { | 38 | struct ProxyPacket { |
| 22 | SockAddrIn local_endpoint; | 39 | SockAddrIn local_endpoint; |
| 23 | SockAddrIn remote_endpoint; | 40 | SockAddrIn remote_endpoint; |
| @@ -152,6 +169,12 @@ public: | |||
| 152 | void SendProxyPacket(const ProxyPacket& packet); | 169 | void SendProxyPacket(const ProxyPacket& packet); |
| 153 | 170 | ||
| 154 | /** | 171 | /** |
| 172 | * Sends an LDN packet to the room. | ||
| 173 | * @param packet The WiFi packet to send. | ||
| 174 | */ | ||
| 175 | void SendLdnPacket(const LDNPacket& packet); | ||
| 176 | |||
| 177 | /** | ||
| 155 | * Sends a chat message to the room. | 178 | * Sends a chat message to the room. |
| 156 | * @param message The contents of the message. | 179 | * @param message The contents of the message. |
| 157 | */ | 180 | */ |
| @@ -205,6 +228,16 @@ public: | |||
| 205 | std::function<void(const ProxyPacket&)> callback); | 228 | std::function<void(const ProxyPacket&)> callback); |
| 206 | 229 | ||
| 207 | /** | 230 | /** |
| 231 | * Binds a function to an event that will be triggered every time an LDNPacket is received. | ||
| 232 | * The function wil be called everytime the event is triggered. | ||
| 233 | * The callback function must not bind or unbind a function. Doing so will cause a deadlock | ||
| 234 | * @param callback The function to call | ||
| 235 | * @return A handle used for removing the function from the registered list | ||
| 236 | */ | ||
| 237 | CallbackHandle<LDNPacket> BindOnLdnPacketReceived( | ||
| 238 | std::function<void(const LDNPacket&)> callback); | ||
| 239 | |||
| 240 | /** | ||
| 208 | * Binds a function to an event that will be triggered every time the RoomInformation changes. | 241 | * Binds a function to an event that will be triggered every time the RoomInformation changes. |
| 209 | * The function wil be called every time the event is triggered. | 242 | * The function wil be called every time the event is triggered. |
| 210 | * The callback function must not bind or unbind a function. Doing so will cause a deadlock | 243 | * The callback function must not bind or unbind a function. Doing so will cause a deadlock |
diff --git a/src/video_core/host_shaders/astc_decoder.comp b/src/video_core/host_shaders/astc_decoder.comp index 3441a5fe5..d608678a3 100644 --- a/src/video_core/host_shaders/astc_decoder.comp +++ b/src/video_core/host_shaders/astc_decoder.comp | |||
| @@ -1065,7 +1065,7 @@ TexelWeightParams DecodeBlockInfo() { | |||
| 1065 | void FillError(ivec3 coord) { | 1065 | void FillError(ivec3 coord) { |
| 1066 | for (uint j = 0; j < block_dims.y; j++) { | 1066 | for (uint j = 0; j < block_dims.y; j++) { |
| 1067 | for (uint i = 0; i < block_dims.x; i++) { | 1067 | for (uint i = 0; i < block_dims.x; i++) { |
| 1068 | imageStore(dest_image, coord + ivec3(i, j, 0), vec4(1.0, 1.0, 0.0, 1.0)); | 1068 | imageStore(dest_image, coord + ivec3(i, j, 0), vec4(0.0, 0.0, 0.0, 0.0)); |
| 1069 | } | 1069 | } |
| 1070 | } | 1070 | } |
| 1071 | } | 1071 | } |
diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp index 58382755b..cabe8dcbf 100644 --- a/src/video_core/macro/macro_hle.cpp +++ b/src/video_core/macro/macro_hle.cpp | |||
| @@ -3,6 +3,8 @@ | |||
| 3 | 3 | ||
| 4 | #include <array> | 4 | #include <array> |
| 5 | #include <vector> | 5 | #include <vector> |
| 6 | #include "common/scope_exit.h" | ||
| 7 | #include "video_core/dirty_flags.h" | ||
| 6 | #include "video_core/engines/maxwell_3d.h" | 8 | #include "video_core/engines/maxwell_3d.h" |
| 7 | #include "video_core/macro/macro.h" | 9 | #include "video_core/macro/macro.h" |
| 8 | #include "video_core/macro/macro_hle.h" | 10 | #include "video_core/macro/macro_hle.h" |
| @@ -58,6 +60,7 @@ void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& | |||
| 58 | maxwell3d.regs.index_array.first = parameters[3]; | 60 | maxwell3d.regs.index_array.first = parameters[3]; |
| 59 | maxwell3d.regs.reg_array[0x446] = element_base; // vertex id base? | 61 | maxwell3d.regs.reg_array[0x446] = element_base; // vertex id base? |
| 60 | maxwell3d.regs.index_array.count = parameters[1]; | 62 | maxwell3d.regs.index_array.count = parameters[1]; |
| 63 | maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; | ||
| 61 | maxwell3d.regs.vb_element_base = element_base; | 64 | maxwell3d.regs.vb_element_base = element_base; |
| 62 | maxwell3d.regs.vb_base_instance = base_instance; | 65 | maxwell3d.regs.vb_base_instance = base_instance; |
| 63 | maxwell3d.mme_draw.instance_count = instance_count; | 66 | maxwell3d.mme_draw.instance_count = instance_count; |
| @@ -80,10 +83,67 @@ void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& | |||
| 80 | maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; | 83 | maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; |
| 81 | } | 84 | } |
| 82 | 85 | ||
| 83 | constexpr std::array<std::pair<u64, HLEFunction>, 3> hle_funcs{{ | 86 | // Multidraw Indirect |
| 87 | void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { | ||
| 88 | SCOPE_EXIT({ | ||
| 89 | // Clean everything. | ||
| 90 | maxwell3d.regs.reg_array[0x446] = 0x0; // vertex id base? | ||
| 91 | maxwell3d.regs.index_array.count = 0; | ||
| 92 | maxwell3d.regs.vb_element_base = 0x0; | ||
| 93 | maxwell3d.regs.vb_base_instance = 0x0; | ||
| 94 | maxwell3d.mme_draw.instance_count = 0; | ||
| 95 | maxwell3d.CallMethodFromMME(0x8e3, 0x640); | ||
| 96 | maxwell3d.CallMethodFromMME(0x8e4, 0x0); | ||
| 97 | maxwell3d.CallMethodFromMME(0x8e5, 0x0); | ||
| 98 | maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; | ||
| 99 | maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; | ||
| 100 | }); | ||
| 101 | const u32 start_indirect = parameters[0]; | ||
| 102 | const u32 end_indirect = parameters[1]; | ||
| 103 | if (start_indirect >= end_indirect) { | ||
| 104 | // Nothing to do. | ||
| 105 | return; | ||
| 106 | } | ||
| 107 | const auto topology = | ||
| 108 | static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[2]); | ||
| 109 | maxwell3d.regs.draw.topology.Assign(topology); | ||
| 110 | const u32 padding = parameters[3]; | ||
| 111 | const std::size_t max_draws = parameters[4]; | ||
| 112 | |||
| 113 | const u32 indirect_words = 5 + padding; | ||
| 114 | const std::size_t first_draw = start_indirect; | ||
| 115 | const std::size_t effective_draws = end_indirect - start_indirect; | ||
| 116 | const std::size_t last_draw = start_indirect + std::min(effective_draws, max_draws); | ||
| 117 | |||
| 118 | for (std::size_t index = first_draw; index < last_draw; index++) { | ||
| 119 | const std::size_t base = index * indirect_words + 5; | ||
| 120 | const u32 num_vertices = parameters[base]; | ||
| 121 | const u32 instance_count = parameters[base + 1]; | ||
| 122 | const u32 first_index = parameters[base + 2]; | ||
| 123 | const u32 base_vertex = parameters[base + 3]; | ||
| 124 | const u32 base_instance = parameters[base + 4]; | ||
| 125 | maxwell3d.regs.index_array.first = first_index; | ||
| 126 | maxwell3d.regs.reg_array[0x446] = base_vertex; | ||
| 127 | maxwell3d.regs.index_array.count = num_vertices; | ||
| 128 | maxwell3d.regs.vb_element_base = base_vertex; | ||
| 129 | maxwell3d.regs.vb_base_instance = base_instance; | ||
| 130 | maxwell3d.mme_draw.instance_count = instance_count; | ||
| 131 | maxwell3d.CallMethodFromMME(0x8e3, 0x640); | ||
| 132 | maxwell3d.CallMethodFromMME(0x8e4, base_vertex); | ||
| 133 | maxwell3d.CallMethodFromMME(0x8e5, base_instance); | ||
| 134 | maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; | ||
| 135 | if (maxwell3d.ShouldExecute()) { | ||
| 136 | maxwell3d.Rasterizer().Draw(true, true); | ||
| 137 | } | ||
| 138 | maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | constexpr std::array<std::pair<u64, HLEFunction>, 4> hle_funcs{{ | ||
| 84 | {0x771BB18C62444DA0, &HLE_771BB18C62444DA0}, | 143 | {0x771BB18C62444DA0, &HLE_771BB18C62444DA0}, |
| 85 | {0x0D61FC9FAAC9FCAD, &HLE_0D61FC9FAAC9FCAD}, | 144 | {0x0D61FC9FAAC9FCAD, &HLE_0D61FC9FAAC9FCAD}, |
| 86 | {0x0217920100488FF7, &HLE_0217920100488FF7}, | 145 | {0x0217920100488FF7, &HLE_0217920100488FF7}, |
| 146 | {0x3F5E74B9C9A50164, &HLE_3F5E74B9C9A50164}, | ||
| 87 | }}; | 147 | }}; |
| 88 | 148 | ||
| 89 | class HLEMacroImpl final : public CachedMacro { | 149 | class HLEMacroImpl final : public CachedMacro { |
| @@ -99,6 +159,7 @@ private: | |||
| 99 | Engines::Maxwell3D& maxwell3d; | 159 | Engines::Maxwell3D& maxwell3d; |
| 100 | HLEFunction func; | 160 | HLEFunction func; |
| 101 | }; | 161 | }; |
| 162 | |||
| 102 | } // Anonymous namespace | 163 | } // Anonymous namespace |
| 103 | 164 | ||
| 104 | HLEMacro::HLEMacro(Engines::Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} {} | 165 | HLEMacro::HLEMacro(Engines::Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} {} |
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index 32450ee1d..08f4d69ab 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp | |||
| @@ -168,7 +168,7 @@ void BufferCacheRuntime::BindIndexBuffer(Buffer& buffer, u32 offset, u32 size) { | |||
| 168 | if (has_unified_vertex_buffers) { | 168 | if (has_unified_vertex_buffers) { |
| 169 | buffer.MakeResident(GL_READ_ONLY); | 169 | buffer.MakeResident(GL_READ_ONLY); |
| 170 | glBufferAddressRangeNV(GL_ELEMENT_ARRAY_ADDRESS_NV, 0, buffer.HostGpuAddr() + offset, | 170 | glBufferAddressRangeNV(GL_ELEMENT_ARRAY_ADDRESS_NV, 0, buffer.HostGpuAddr() + offset, |
| 171 | static_cast<GLsizeiptr>(size)); | 171 | static_cast<GLsizeiptr>(Common::AlignUp(size, 4))); |
| 172 | } else { | 172 | } else { |
| 173 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.Handle()); | 173 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.Handle()); |
| 174 | index_buffer_offset = offset; | 174 | index_buffer_offset = offset; |
diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp index b159494c5..e3742ddf5 100644 --- a/src/video_core/textures/astc.cpp +++ b/src/video_core/textures/astc.cpp | |||
| @@ -1413,7 +1413,7 @@ static void FillVoidExtentLDR(InputBitStream& strm, std::span<u32> outBuf, u32 b | |||
| 1413 | static void FillError(std::span<u32> outBuf, u32 blockWidth, u32 blockHeight) { | 1413 | static void FillError(std::span<u32> outBuf, u32 blockWidth, u32 blockHeight) { |
| 1414 | for (u32 j = 0; j < blockHeight; j++) { | 1414 | for (u32 j = 0; j < blockHeight; j++) { |
| 1415 | for (u32 i = 0; i < blockWidth; i++) { | 1415 | for (u32 i = 0; i < blockWidth; i++) { |
| 1416 | outBuf[j * blockWidth + i] = 0xFFFF00FF; | 1416 | outBuf[j * blockWidth + i] = 0x00000000; |
| 1417 | } | 1417 | } |
| 1418 | } | 1418 | } |
| 1419 | } | 1419 | } |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 632f7c9c9..c63ce3a30 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -899,8 +899,8 @@ void GMainWindow::InitializeWidgets() { | |||
| 899 | } | 899 | } |
| 900 | 900 | ||
| 901 | // TODO (flTobi): Add the widget when multiplayer is fully implemented | 901 | // TODO (flTobi): Add the widget when multiplayer is fully implemented |
| 902 | // statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0); | 902 | statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0); |
| 903 | // statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0); | 903 | statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0); |
| 904 | 904 | ||
| 905 | tas_label = new QLabel(); | 905 | tas_label = new QLabel(); |
| 906 | tas_label->setObjectName(QStringLiteral("TASlabel")); | 906 | tas_label->setObjectName(QStringLiteral("TASlabel")); |
| @@ -1299,6 +1299,7 @@ void GMainWindow::ConnectMenuEvents() { | |||
| 1299 | &MultiplayerState::OnDirectConnectToRoom); | 1299 | &MultiplayerState::OnDirectConnectToRoom); |
| 1300 | connect(ui->action_Show_Room, &QAction::triggered, multiplayer_state, | 1300 | connect(ui->action_Show_Room, &QAction::triggered, multiplayer_state, |
| 1301 | &MultiplayerState::OnOpenNetworkRoom); | 1301 | &MultiplayerState::OnOpenNetworkRoom); |
| 1302 | connect(multiplayer_state, &MultiplayerState::SaveConfig, this, &GMainWindow::OnSaveConfig); | ||
| 1302 | 1303 | ||
| 1303 | // Tools | 1304 | // Tools |
| 1304 | connect_menu(ui->action_Rederive, std::bind(&GMainWindow::OnReinitializeKeys, this, | 1305 | connect_menu(ui->action_Rederive, std::bind(&GMainWindow::OnReinitializeKeys, this, |
| @@ -1339,6 +1340,8 @@ void GMainWindow::UpdateMenuState() { | |||
| 1339 | } else { | 1340 | } else { |
| 1340 | ui->action_Pause->setText(tr("&Pause")); | 1341 | ui->action_Pause->setText(tr("&Pause")); |
| 1341 | } | 1342 | } |
| 1343 | |||
| 1344 | multiplayer_state->UpdateNotificationStatus(); | ||
| 1342 | } | 1345 | } |
| 1343 | 1346 | ||
| 1344 | void GMainWindow::OnDisplayTitleBars(bool show) { | 1347 | void GMainWindow::OnDisplayTitleBars(bool show) { |
| @@ -2770,6 +2773,11 @@ void GMainWindow::OnExit() { | |||
| 2770 | OnStopGame(); | 2773 | OnStopGame(); |
| 2771 | } | 2774 | } |
| 2772 | 2775 | ||
| 2776 | void GMainWindow::OnSaveConfig() { | ||
| 2777 | system->ApplySettings(); | ||
| 2778 | config->Save(); | ||
| 2779 | } | ||
| 2780 | |||
| 2773 | void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) { | 2781 | void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) { |
| 2774 | OverlayDialog dialog(render_window, *system, error_code, error_text, QString{}, tr("OK"), | 2782 | OverlayDialog dialog(render_window, *system, error_code, error_text, QString{}, tr("OK"), |
| 2775 | Qt::AlignLeft | Qt::AlignVCenter); | 2783 | Qt::AlignLeft | Qt::AlignVCenter); |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 716aef063..f7aa8e417 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -169,6 +169,7 @@ public slots: | |||
| 169 | void OnLoadComplete(); | 169 | void OnLoadComplete(); |
| 170 | void OnExecuteProgram(std::size_t program_index); | 170 | void OnExecuteProgram(std::size_t program_index); |
| 171 | void OnExit(); | 171 | void OnExit(); |
| 172 | void OnSaveConfig(); | ||
| 172 | void ControllerSelectorReconfigureControllers( | 173 | void ControllerSelectorReconfigureControllers( |
| 173 | const Core::Frontend::ControllerParameters& parameters); | 174 | const Core::Frontend::ControllerParameters& parameters); |
| 174 | void SoftwareKeyboardInitialize( | 175 | void SoftwareKeyboardInitialize( |
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index cdf31b417..74d49dbd4 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui | |||
| @@ -120,6 +120,20 @@ | |||
| 120 | <addaction name="menu_Reset_Window_Size"/> | 120 | <addaction name="menu_Reset_Window_Size"/> |
| 121 | <addaction name="menu_View_Debugging"/> | 121 | <addaction name="menu_View_Debugging"/> |
| 122 | </widget> | 122 | </widget> |
| 123 | <widget class="QMenu" name="menu_Multiplayer"> | ||
| 124 | <property name="enabled"> | ||
| 125 | <bool>true</bool> | ||
| 126 | </property> | ||
| 127 | <property name="title"> | ||
| 128 | <string>&Multiplayer</string> | ||
| 129 | </property> | ||
| 130 | <addaction name="action_View_Lobby"/> | ||
| 131 | <addaction name="action_Start_Room"/> | ||
| 132 | <addaction name="action_Connect_To_Room"/> | ||
| 133 | <addaction name="separator"/> | ||
| 134 | <addaction name="action_Show_Room"/> | ||
| 135 | <addaction name="action_Leave_Room"/> | ||
| 136 | </widget> | ||
| 123 | <widget class="QMenu" name="menu_Tools"> | 137 | <widget class="QMenu" name="menu_Tools"> |
| 124 | <property name="title"> | 138 | <property name="title"> |
| 125 | <string>&Tools</string> | 139 | <string>&Tools</string> |
| @@ -251,7 +265,7 @@ | |||
| 251 | <bool>true</bool> | 265 | <bool>true</bool> |
| 252 | </property> | 266 | </property> |
| 253 | <property name="text"> | 267 | <property name="text"> |
| 254 | <string>Browse Public Game Lobby</string> | 268 | <string>&Browse Public Game Lobby</string> |
| 255 | </property> | 269 | </property> |
| 256 | </action> | 270 | </action> |
| 257 | <action name="action_Start_Room"> | 271 | <action name="action_Start_Room"> |
| @@ -259,7 +273,7 @@ | |||
| 259 | <bool>true</bool> | 273 | <bool>true</bool> |
| 260 | </property> | 274 | </property> |
| 261 | <property name="text"> | 275 | <property name="text"> |
| 262 | <string>Create Room</string> | 276 | <string>&Create Room</string> |
| 263 | </property> | 277 | </property> |
| 264 | </action> | 278 | </action> |
| 265 | <action name="action_Leave_Room"> | 279 | <action name="action_Leave_Room"> |
| @@ -267,12 +281,12 @@ | |||
| 267 | <bool>false</bool> | 281 | <bool>false</bool> |
| 268 | </property> | 282 | </property> |
| 269 | <property name="text"> | 283 | <property name="text"> |
| 270 | <string>Leave Room</string> | 284 | <string>&Leave Room</string> |
| 271 | </property> | 285 | </property> |
| 272 | </action> | 286 | </action> |
| 273 | <action name="action_Connect_To_Room"> | 287 | <action name="action_Connect_To_Room"> |
| 274 | <property name="text"> | 288 | <property name="text"> |
| 275 | <string>Direct Connect to Room</string> | 289 | <string>&Direct Connect to Room</string> |
| 276 | </property> | 290 | </property> |
| 277 | </action> | 291 | </action> |
| 278 | <action name="action_Show_Room"> | 292 | <action name="action_Show_Room"> |
| @@ -280,7 +294,7 @@ | |||
| 280 | <bool>false</bool> | 294 | <bool>false</bool> |
| 281 | </property> | 295 | </property> |
| 282 | <property name="text"> | 296 | <property name="text"> |
| 283 | <string>Show Current Room</string> | 297 | <string>&Show Current Room</string> |
| 284 | </property> | 298 | </property> |
| 285 | </action> | 299 | </action> |
| 286 | <action name="action_Fullscreen"> | 300 | <action name="action_Fullscreen"> |
diff --git a/src/yuzu/multiplayer/chat_room.cpp b/src/yuzu/multiplayer/chat_room.cpp index 9e672f82e..dec9696c1 100644 --- a/src/yuzu/multiplayer/chat_room.cpp +++ b/src/yuzu/multiplayer/chat_room.cpp | |||
| @@ -61,7 +61,10 @@ public: | |||
| 61 | 61 | ||
| 62 | /// Format the message using the players color | 62 | /// Format the message using the players color |
| 63 | QString GetPlayerChatMessage(u16 player) const { | 63 | QString GetPlayerChatMessage(u16 player) const { |
| 64 | auto color = player_color[player % 16]; | 64 | const bool is_dark_theme = QIcon::themeName().contains(QStringLiteral("dark")) || |
| 65 | QIcon::themeName().contains(QStringLiteral("midnight")); | ||
| 66 | auto color = | ||
| 67 | is_dark_theme ? player_color_dark[player % 16] : player_color_default[player % 16]; | ||
| 65 | QString name; | 68 | QString name; |
| 66 | if (username.isEmpty() || username == nickname) { | 69 | if (username.isEmpty() || username == nickname) { |
| 67 | name = nickname; | 70 | name = nickname; |
| @@ -84,9 +87,12 @@ public: | |||
| 84 | } | 87 | } |
| 85 | 88 | ||
| 86 | private: | 89 | private: |
| 87 | static constexpr std::array<const char*, 16> player_color = { | 90 | static constexpr std::array<const char*, 16> player_color_default = { |
| 88 | {"#0000FF", "#FF0000", "#8A2BE2", "#FF69B4", "#1E90FF", "#008000", "#00FF7F", "#B22222", | 91 | {"#0000FF", "#FF0000", "#8A2BE2", "#FF69B4", "#1E90FF", "#008000", "#00FF7F", "#B22222", |
| 89 | "#DAA520", "#FF4500", "#2E8B57", "#5F9EA0", "#D2691E", "#9ACD32", "#FF7F50", "FFFF00"}}; | 92 | "#DAA520", "#FF4500", "#2E8B57", "#5F9EA0", "#D2691E", "#9ACD32", "#FF7F50", "#FFFF00"}}; |
| 93 | static constexpr std::array<const char*, 16> player_color_dark = { | ||
| 94 | {"#559AD1", "#4EC9A8", "#D69D85", "#C6C923", "#B975B5", "#D81F1F", "#7EAE39", "#4F8733", | ||
| 95 | "#F7CD8A", "#6FCACF", "#CE4897", "#8A2BE2", "#D2691E", "#9ACD32", "#FF7F50", "#152ccd"}}; | ||
| 90 | static constexpr char ping_color[] = "#FFFF00"; | 96 | static constexpr char ping_color[] = "#FFFF00"; |
| 91 | 97 | ||
| 92 | QString timestamp; | 98 | QString timestamp; |
diff --git a/src/yuzu/multiplayer/client_room.cpp b/src/yuzu/multiplayer/client_room.cpp index b34a8d004..caf34a414 100644 --- a/src/yuzu/multiplayer/client_room.cpp +++ b/src/yuzu/multiplayer/client_room.cpp | |||
| @@ -97,8 +97,9 @@ void ClientRoomWindow::UpdateView() { | |||
| 97 | auto memberlist = member->GetMemberInformation(); | 97 | auto memberlist = member->GetMemberInformation(); |
| 98 | ui->chat->SetPlayerList(memberlist); | 98 | ui->chat->SetPlayerList(memberlist); |
| 99 | const auto information = member->GetRoomInformation(); | 99 | const auto information = member->GetRoomInformation(); |
| 100 | setWindowTitle(QString(tr("%1 (%2/%3 members) - connected")) | 100 | setWindowTitle(QString(tr("%1 - %2 (%3/%4 members) - connected")) |
| 101 | .arg(QString::fromStdString(information.name)) | 101 | .arg(QString::fromStdString(information.name)) |
| 102 | .arg(QString::fromStdString(information.preferred_game.name)) | ||
| 102 | .arg(memberlist.size()) | 103 | .arg(memberlist.size()) |
| 103 | .arg(information.member_slots)); | 104 | .arg(information.member_slots)); |
| 104 | ui->description->setText(QString::fromStdString(information.description)); | 105 | ui->description->setText(QString::fromStdString(information.description)); |
diff --git a/src/yuzu/multiplayer/direct_connect.cpp b/src/yuzu/multiplayer/direct_connect.cpp index 017063074..10bf0a4fb 100644 --- a/src/yuzu/multiplayer/direct_connect.cpp +++ b/src/yuzu/multiplayer/direct_connect.cpp | |||
| @@ -106,6 +106,8 @@ void DirectConnectWindow::Connect() { | |||
| 106 | UISettings::values.multiplayer_port = UISettings::values.multiplayer_port.GetDefault(); | 106 | UISettings::values.multiplayer_port = UISettings::values.multiplayer_port.GetDefault(); |
| 107 | } | 107 | } |
| 108 | 108 | ||
| 109 | emit SaveConfig(); | ||
| 110 | |||
| 109 | // attempt to connect in a different thread | 111 | // attempt to connect in a different thread |
| 110 | QFuture<void> f = QtConcurrent::run([&] { | 112 | QFuture<void> f = QtConcurrent::run([&] { |
| 111 | if (auto room_member = room_network.GetRoomMember().lock()) { | 113 | 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 e39dd1e0d..b8f66cfb2 100644 --- a/src/yuzu/multiplayer/direct_connect.h +++ b/src/yuzu/multiplayer/direct_connect.h | |||
| @@ -31,6 +31,7 @@ signals: | |||
| 31 | * connections that it might have. | 31 | * connections that it might have. |
| 32 | */ | 32 | */ |
| 33 | void Closed(); | 33 | void Closed(); |
| 34 | void SaveConfig(); | ||
| 34 | 35 | ||
| 35 | private slots: | 36 | private slots: |
| 36 | void OnConnection(); | 37 | void OnConnection(); |
diff --git a/src/yuzu/multiplayer/host_room.cpp b/src/yuzu/multiplayer/host_room.cpp index 0c6adfd04..a8faa5b24 100644 --- a/src/yuzu/multiplayer/host_room.cpp +++ b/src/yuzu/multiplayer/host_room.cpp | |||
| @@ -232,6 +232,7 @@ void HostRoomWindow::Host() { | |||
| 232 | } | 232 | } |
| 233 | UISettings::values.multiplayer_room_description = ui->room_description->toPlainText(); | 233 | UISettings::values.multiplayer_room_description = ui->room_description->toPlainText(); |
| 234 | ui->host->setEnabled(true); | 234 | ui->host->setEnabled(true); |
| 235 | emit SaveConfig(); | ||
| 235 | close(); | 236 | close(); |
| 236 | } | 237 | } |
| 237 | } | 238 | } |
diff --git a/src/yuzu/multiplayer/host_room.h b/src/yuzu/multiplayer/host_room.h index 034cb2eef..ae816e2e0 100644 --- a/src/yuzu/multiplayer/host_room.h +++ b/src/yuzu/multiplayer/host_room.h | |||
| @@ -46,6 +46,9 @@ public: | |||
| 46 | void UpdateGameList(QStandardItemModel* list); | 46 | void UpdateGameList(QStandardItemModel* list); |
| 47 | void RetranslateUi(); | 47 | void RetranslateUi(); |
| 48 | 48 | ||
| 49 | signals: | ||
| 50 | void SaveConfig(); | ||
| 51 | |||
| 49 | private: | 52 | private: |
| 50 | void Host(); | 53 | void Host(); |
| 51 | std::unique_ptr<Network::VerifyUser::Backend> CreateVerifyBackend(bool use_validation) const; | 54 | 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 107d40547..08c275696 100644 --- a/src/yuzu/multiplayer/lobby.cpp +++ b/src/yuzu/multiplayer/lobby.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | #include "common/settings.h" | 8 | #include "common/settings.h" |
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/hle/service/acc/profile_manager.h" | ||
| 10 | #include "core/internal_network/network_interface.h" | 11 | #include "core/internal_network/network_interface.h" |
| 11 | #include "network/network.h" | 12 | #include "network/network.h" |
| 12 | #include "ui_lobby.h" | 13 | #include "ui_lobby.h" |
| @@ -26,9 +27,9 @@ | |||
| 26 | Lobby::Lobby(QWidget* parent, QStandardItemModel* list, | 27 | Lobby::Lobby(QWidget* parent, QStandardItemModel* list, |
| 27 | std::shared_ptr<Core::AnnounceMultiplayerSession> session, Core::System& system_) | 28 | std::shared_ptr<Core::AnnounceMultiplayerSession> session, Core::System& system_) |
| 28 | : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), | 29 | : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), |
| 29 | ui(std::make_unique<Ui::Lobby>()), | 30 | ui(std::make_unique<Ui::Lobby>()), announce_multiplayer_session(session), |
| 30 | announce_multiplayer_session(session), system{system_}, room_network{ | 31 | profile_manager(std::make_unique<Service::Account::ProfileManager>()), system{system_}, |
| 31 | system.GetRoomNetwork()} { | 32 | room_network{system.GetRoomNetwork()} { |
| 32 | ui->setupUi(this); | 33 | ui->setupUi(this); |
| 33 | 34 | ||
| 34 | // setup the watcher for background connections | 35 | // setup the watcher for background connections |
| @@ -60,9 +61,17 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list, | |||
| 60 | 61 | ||
| 61 | ui->nickname->setValidator(validation.GetNickname()); | 62 | ui->nickname->setValidator(validation.GetNickname()); |
| 62 | ui->nickname->setText(UISettings::values.multiplayer_nickname.GetValue()); | 63 | ui->nickname->setText(UISettings::values.multiplayer_nickname.GetValue()); |
| 63 | if (ui->nickname->text().isEmpty() && !Settings::values.yuzu_username.GetValue().empty()) { | 64 | |
| 64 | // Use yuzu Web Service user name as nickname by default | 65 | // Try find the best nickname by default |
| 65 | ui->nickname->setText(QString::fromStdString(Settings::values.yuzu_username.GetValue())); | 66 | if (ui->nickname->text().isEmpty() || ui->nickname->text() == QStringLiteral("yuzu")) { |
| 67 | if (!Settings::values.yuzu_username.GetValue().empty()) { | ||
| 68 | ui->nickname->setText( | ||
| 69 | QString::fromStdString(Settings::values.yuzu_username.GetValue())); | ||
| 70 | } else if (!GetProfileUsername().empty()) { | ||
| 71 | ui->nickname->setText(QString::fromStdString(GetProfileUsername())); | ||
| 72 | } else { | ||
| 73 | ui->nickname->setText(QStringLiteral("yuzu")); | ||
| 74 | } | ||
| 66 | } | 75 | } |
| 67 | 76 | ||
| 68 | // UI Buttons | 77 | // UI Buttons |
| @@ -76,12 +85,6 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list, | |||
| 76 | // Actions | 85 | // Actions |
| 77 | connect(&room_list_watcher, &QFutureWatcher<AnnounceMultiplayerRoom::RoomList>::finished, this, | 86 | connect(&room_list_watcher, &QFutureWatcher<AnnounceMultiplayerRoom::RoomList>::finished, this, |
| 78 | &Lobby::OnRefreshLobby); | 87 | &Lobby::OnRefreshLobby); |
| 79 | |||
| 80 | // manually start a refresh when the window is opening | ||
| 81 | // TODO(jroweboy): if this refresh is slow for people with bad internet, then don't do it as | ||
| 82 | // part of the constructor, but offload the refresh until after the window shown. perhaps emit a | ||
| 83 | // refreshroomlist signal from places that open the lobby | ||
| 84 | RefreshLobby(); | ||
| 85 | } | 88 | } |
| 86 | 89 | ||
| 87 | Lobby::~Lobby() = default; | 90 | Lobby::~Lobby() = default; |
| @@ -96,6 +99,7 @@ void Lobby::UpdateGameList(QStandardItemModel* list) { | |||
| 96 | } | 99 | } |
| 97 | if (proxy) | 100 | if (proxy) |
| 98 | proxy->UpdateGameList(game_list); | 101 | proxy->UpdateGameList(game_list); |
| 102 | ui->room_list->sortByColumn(Column::GAME_NAME, Qt::AscendingOrder); | ||
| 99 | } | 103 | } |
| 100 | 104 | ||
| 101 | void Lobby::RetranslateUi() { | 105 | void Lobby::RetranslateUi() { |
| @@ -117,6 +121,11 @@ void Lobby::OnExpandRoom(const QModelIndex& index) { | |||
| 117 | 121 | ||
| 118 | void Lobby::OnJoinRoom(const QModelIndex& source) { | 122 | void Lobby::OnJoinRoom(const QModelIndex& source) { |
| 119 | if (!Network::GetSelectedNetworkInterface()) { | 123 | if (!Network::GetSelectedNetworkInterface()) { |
| 124 | LOG_INFO(WebService, "Automatically selected network interface for room network."); | ||
| 125 | Network::SelectFirstNetworkInterface(); | ||
| 126 | } | ||
| 127 | |||
| 128 | if (!Network::GetSelectedNetworkInterface()) { | ||
| 120 | NetworkMessage::ErrorManager::ShowError( | 129 | NetworkMessage::ErrorManager::ShowError( |
| 121 | NetworkMessage::ErrorManager::NO_INTERFACE_SELECTED); | 130 | NetworkMessage::ErrorManager::NO_INTERFACE_SELECTED); |
| 122 | return; | 131 | return; |
| @@ -197,16 +206,16 @@ void Lobby::OnJoinRoom(const QModelIndex& source) { | |||
| 197 | proxy->data(connection_index, LobbyItemHost::HostIPRole).toString(); | 206 | proxy->data(connection_index, LobbyItemHost::HostIPRole).toString(); |
| 198 | UISettings::values.multiplayer_port = | 207 | UISettings::values.multiplayer_port = |
| 199 | proxy->data(connection_index, LobbyItemHost::HostPortRole).toInt(); | 208 | proxy->data(connection_index, LobbyItemHost::HostPortRole).toInt(); |
| 209 | emit SaveConfig(); | ||
| 200 | } | 210 | } |
| 201 | 211 | ||
| 202 | void Lobby::ResetModel() { | 212 | void Lobby::ResetModel() { |
| 203 | model->clear(); | 213 | model->clear(); |
| 204 | model->insertColumns(0, Column::TOTAL); | 214 | model->insertColumns(0, Column::TOTAL); |
| 205 | model->setHeaderData(Column::EXPAND, Qt::Horizontal, QString(), Qt::DisplayRole); | 215 | model->setHeaderData(Column::MEMBER, Qt::Horizontal, tr("Players"), Qt::DisplayRole); |
| 206 | model->setHeaderData(Column::ROOM_NAME, Qt::Horizontal, tr("Room Name"), Qt::DisplayRole); | 216 | model->setHeaderData(Column::ROOM_NAME, Qt::Horizontal, tr("Room Name"), Qt::DisplayRole); |
| 207 | model->setHeaderData(Column::GAME_NAME, Qt::Horizontal, tr("Preferred Game"), Qt::DisplayRole); | 217 | model->setHeaderData(Column::GAME_NAME, Qt::Horizontal, tr("Preferred Game"), Qt::DisplayRole); |
| 208 | model->setHeaderData(Column::HOST, Qt::Horizontal, tr("Host"), Qt::DisplayRole); | 218 | model->setHeaderData(Column::HOST, Qt::Horizontal, tr("Host"), Qt::DisplayRole); |
| 209 | model->setHeaderData(Column::MEMBER, Qt::Horizontal, tr("Players"), Qt::DisplayRole); | ||
| 210 | } | 219 | } |
| 211 | 220 | ||
| 212 | void Lobby::RefreshLobby() { | 221 | void Lobby::RefreshLobby() { |
| @@ -229,6 +238,7 @@ void Lobby::OnRefreshLobby() { | |||
| 229 | for (int r = 0; r < game_list->rowCount(); ++r) { | 238 | for (int r = 0; r < game_list->rowCount(); ++r) { |
| 230 | auto index = game_list->index(r, 0); | 239 | auto index = game_list->index(r, 0); |
| 231 | auto game_id = game_list->data(index, GameListItemPath::ProgramIdRole).toULongLong(); | 240 | auto game_id = game_list->data(index, GameListItemPath::ProgramIdRole).toULongLong(); |
| 241 | |||
| 232 | if (game_id != 0 && room.information.preferred_game.id == game_id) { | 242 | if (game_id != 0 && room.information.preferred_game.id == game_id) { |
| 233 | smdh_icon = game_list->data(index, Qt::DecorationRole).value<QPixmap>(); | 243 | smdh_icon = game_list->data(index, Qt::DecorationRole).value<QPixmap>(); |
| 234 | } | 244 | } |
| @@ -243,17 +253,16 @@ void Lobby::OnRefreshLobby() { | |||
| 243 | members.append(var); | 253 | members.append(var); |
| 244 | } | 254 | } |
| 245 | 255 | ||
| 246 | auto first_item = new LobbyItem(); | 256 | auto first_item = new LobbyItemGame( |
| 257 | room.information.preferred_game.id, | ||
| 258 | QString::fromStdString(room.information.preferred_game.name), smdh_icon); | ||
| 247 | auto row = QList<QStandardItem*>({ | 259 | auto row = QList<QStandardItem*>({ |
| 248 | first_item, | 260 | first_item, |
| 249 | new LobbyItemName(room.has_password, QString::fromStdString(room.information.name)), | 261 | new LobbyItemName(room.has_password, QString::fromStdString(room.information.name)), |
| 250 | new LobbyItemGame(room.information.preferred_game.id, | 262 | new LobbyItemMemberList(members, room.information.member_slots), |
| 251 | QString::fromStdString(room.information.preferred_game.name), | ||
| 252 | smdh_icon), | ||
| 253 | new LobbyItemHost(QString::fromStdString(room.information.host_username), | 263 | new LobbyItemHost(QString::fromStdString(room.information.host_username), |
| 254 | QString::fromStdString(room.ip), room.information.port, | 264 | QString::fromStdString(room.ip), room.information.port, |
| 255 | QString::fromStdString(room.verify_uid)), | 265 | QString::fromStdString(room.verify_uid)), |
| 256 | new LobbyItemMemberList(members, room.information.member_slots), | ||
| 257 | }); | 266 | }); |
| 258 | model->appendRow(row); | 267 | model->appendRow(row); |
| 259 | // To make the rows expandable, add the member data as a child of the first column of the | 268 | // 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() { | |||
| 283 | ui->room_list->setFirstColumnSpanned(j, proxy->index(i, 0), true); | 292 | ui->room_list->setFirstColumnSpanned(j, proxy->index(i, 0), true); |
| 284 | } | 293 | } |
| 285 | } | 294 | } |
| 295 | |||
| 296 | ui->room_list->sortByColumn(Column::GAME_NAME, Qt::AscendingOrder); | ||
| 297 | } | ||
| 298 | |||
| 299 | std::string Lobby::GetProfileUsername() { | ||
| 300 | const auto& current_user = profile_manager->GetUser(Settings::values.current_user.GetValue()); | ||
| 301 | Service::Account::ProfileBase profile{}; | ||
| 302 | |||
| 303 | if (!current_user.has_value()) { | ||
| 304 | return ""; | ||
| 305 | } | ||
| 306 | |||
| 307 | if (!profile_manager->GetProfileBase(*current_user, profile)) { | ||
| 308 | return ""; | ||
| 309 | } | ||
| 310 | |||
| 311 | const auto text = Common::StringFromFixedZeroTerminatedBuffer( | ||
| 312 | reinterpret_cast<const char*>(profile.username.data()), profile.username.size()); | ||
| 313 | |||
| 314 | return text; | ||
| 286 | } | 315 | } |
| 287 | 316 | ||
| 288 | LobbyFilterProxyModel::LobbyFilterProxyModel(QWidget* parent, QStandardItemModel* list) | 317 | LobbyFilterProxyModel::LobbyFilterProxyModel(QWidget* parent, QStandardItemModel* list) |
diff --git a/src/yuzu/multiplayer/lobby.h b/src/yuzu/multiplayer/lobby.h index 2696aec21..300dad13e 100644 --- a/src/yuzu/multiplayer/lobby.h +++ b/src/yuzu/multiplayer/lobby.h | |||
| @@ -24,6 +24,10 @@ namespace Core { | |||
| 24 | class System; | 24 | class System; |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | namespace Service::Account { | ||
| 28 | class ProfileManager; | ||
| 29 | } | ||
| 30 | |||
| 27 | /** | 31 | /** |
| 28 | * Listing of all public games pulled from services. The lobby should be simple enough for users to | 32 | * Listing of all public games pulled from services. The lobby should be simple enough for users to |
| 29 | * find the game they want to play, and join it. | 33 | * find the game they want to play, and join it. |
| @@ -75,8 +79,11 @@ private slots: | |||
| 75 | 79 | ||
| 76 | signals: | 80 | signals: |
| 77 | void StateChanged(const Network::RoomMember::State&); | 81 | void StateChanged(const Network::RoomMember::State&); |
| 82 | void SaveConfig(); | ||
| 78 | 83 | ||
| 79 | private: | 84 | private: |
| 85 | std::string GetProfileUsername(); | ||
| 86 | |||
| 80 | /** | 87 | /** |
| 81 | * Removes all entries in the Lobby before refreshing. | 88 | * Removes all entries in the Lobby before refreshing. |
| 82 | */ | 89 | */ |
| @@ -96,6 +103,7 @@ private: | |||
| 96 | 103 | ||
| 97 | QFutureWatcher<AnnounceMultiplayerRoom::RoomList> room_list_watcher; | 104 | QFutureWatcher<AnnounceMultiplayerRoom::RoomList> room_list_watcher; |
| 98 | std::weak_ptr<Core::AnnounceMultiplayerSession> announce_multiplayer_session; | 105 | std::weak_ptr<Core::AnnounceMultiplayerSession> announce_multiplayer_session; |
| 106 | std::unique_ptr<Service::Account::ProfileManager> profile_manager; | ||
| 99 | QFutureWatcher<void>* watcher; | 107 | QFutureWatcher<void>* watcher; |
| 100 | Validation validation; | 108 | Validation validation; |
| 101 | Core::System& system; | 109 | Core::System& system; |
diff --git a/src/yuzu/multiplayer/lobby_p.h b/src/yuzu/multiplayer/lobby_p.h index 8071cede4..068c95aca 100644 --- a/src/yuzu/multiplayer/lobby_p.h +++ b/src/yuzu/multiplayer/lobby_p.h | |||
| @@ -11,11 +11,10 @@ | |||
| 11 | 11 | ||
| 12 | namespace Column { | 12 | namespace Column { |
| 13 | enum List { | 13 | enum List { |
| 14 | EXPAND, | ||
| 15 | ROOM_NAME, | ||
| 16 | GAME_NAME, | 14 | GAME_NAME, |
| 17 | HOST, | 15 | ROOM_NAME, |
| 18 | MEMBER, | 16 | MEMBER, |
| 17 | HOST, | ||
| 19 | TOTAL, | 18 | TOTAL, |
| 20 | }; | 19 | }; |
| 21 | } | 20 | } |
| @@ -91,6 +90,8 @@ public: | |||
| 91 | setData(game_name, GameNameRole); | 90 | setData(game_name, GameNameRole); |
| 92 | if (!smdh_icon.isNull()) { | 91 | if (!smdh_icon.isNull()) { |
| 93 | setData(smdh_icon, GameIconRole); | 92 | setData(smdh_icon, GameIconRole); |
| 93 | } else { | ||
| 94 | setData(QIcon::fromTheme(QStringLiteral("chip")).pixmap(32), GameIconRole); | ||
| 94 | } | 95 | } |
| 95 | } | 96 | } |
| 96 | 97 | ||
| @@ -98,7 +99,12 @@ public: | |||
| 98 | if (role == Qt::DecorationRole) { | 99 | if (role == Qt::DecorationRole) { |
| 99 | auto val = data(GameIconRole); | 100 | auto val = data(GameIconRole); |
| 100 | if (val.isValid()) { | 101 | if (val.isValid()) { |
| 101 | val = val.value<QPixmap>().scaled(16, 16, Qt::KeepAspectRatio); | 102 | val = val.value<QPixmap>().scaled(32, 32, Qt::KeepAspectRatio, |
| 103 | Qt::TransformationMode::SmoothTransformation); | ||
| 104 | } else { | ||
| 105 | auto blank_image = QPixmap(32, 32); | ||
| 106 | blank_image.fill(Qt::black); | ||
| 107 | val = blank_image; | ||
| 102 | } | 108 | } |
| 103 | return val; | 109 | return val; |
| 104 | } else if (role != Qt::DisplayRole) { | 110 | } else if (role != Qt::DisplayRole) { |
| @@ -191,8 +197,8 @@ public: | |||
| 191 | return LobbyItem::data(role); | 197 | return LobbyItem::data(role); |
| 192 | } | 198 | } |
| 193 | auto members = data(MemberListRole).toList(); | 199 | auto members = data(MemberListRole).toList(); |
| 194 | return QStringLiteral("%1 / %2").arg(QString::number(members.size()), | 200 | return QStringLiteral("%1 / %2 ") |
| 195 | data(MaxPlayerRole).toString()); | 201 | .arg(QString::number(members.size()), data(MaxPlayerRole).toString()); |
| 196 | } | 202 | } |
| 197 | 203 | ||
| 198 | bool operator<(const QStandardItem& other) const override { | 204 | bool operator<(const QStandardItem& other) const override { |
diff --git a/src/yuzu/multiplayer/message.cpp b/src/yuzu/multiplayer/message.cpp index 758b5b731..6d8f18274 100644 --- a/src/yuzu/multiplayer/message.cpp +++ b/src/yuzu/multiplayer/message.cpp | |||
| @@ -49,9 +49,9 @@ const ConnectionError ErrorManager::PERMISSION_DENIED( | |||
| 49 | QT_TR_NOOP("You do not have enough permission to perform this action.")); | 49 | QT_TR_NOOP("You do not have enough permission to perform this action.")); |
| 50 | const ConnectionError ErrorManager::NO_SUCH_USER(QT_TR_NOOP( | 50 | const ConnectionError ErrorManager::NO_SUCH_USER(QT_TR_NOOP( |
| 51 | "The user you are trying to kick/ban could not be found.\nThey may have left the room.")); | 51 | "The user you are trying to kick/ban could not be found.\nThey may have left the room.")); |
| 52 | const ConnectionError ErrorManager::NO_INTERFACE_SELECTED( | 52 | const ConnectionError ErrorManager::NO_INTERFACE_SELECTED(QT_TR_NOOP( |
| 53 | QT_TR_NOOP("No network interface is selected.\nPlease go to Configure -> System -> Network and " | 53 | "No valid network interface is selected.\nPlease go to Configure -> System -> Network and " |
| 54 | "make a selection.")); | 54 | "make a selection.")); |
| 55 | 55 | ||
| 56 | static bool WarnMessage(const std::string& title, const std::string& text) { | 56 | static bool WarnMessage(const std::string& title, const std::string& text) { |
| 57 | return QMessageBox::Ok == QMessageBox::warning(nullptr, QObject::tr(title.c_str()), | 57 | 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 66e098296..ae2738ad4 100644 --- a/src/yuzu/multiplayer/state.cpp +++ b/src/yuzu/multiplayer/state.cpp | |||
| @@ -44,9 +44,6 @@ MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_lis | |||
| 44 | 44 | ||
| 45 | status_text = new ClickableLabel(this); | 45 | status_text = new ClickableLabel(this); |
| 46 | status_icon = new ClickableLabel(this); | 46 | status_icon = new ClickableLabel(this); |
| 47 | status_text->setToolTip(tr("Current connection status")); | ||
| 48 | status_text->setText(tr("Not Connected. Click here to find a room!")); | ||
| 49 | status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16)); | ||
| 50 | 47 | ||
| 51 | connect(status_text, &ClickableLabel::clicked, this, &MultiplayerState::OnOpenNetworkRoom); | 48 | connect(status_text, &ClickableLabel::clicked, this, &MultiplayerState::OnOpenNetworkRoom); |
| 52 | connect(status_icon, &ClickableLabel::clicked, this, &MultiplayerState::OnOpenNetworkRoom); | 49 | connect(status_icon, &ClickableLabel::clicked, this, &MultiplayerState::OnOpenNetworkRoom); |
| @@ -57,6 +54,8 @@ MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_lis | |||
| 57 | HideNotification(); | 54 | HideNotification(); |
| 58 | } | 55 | } |
| 59 | }); | 56 | }); |
| 57 | |||
| 58 | retranslateUi(); | ||
| 60 | } | 59 | } |
| 61 | 60 | ||
| 62 | MultiplayerState::~MultiplayerState() = default; | 61 | MultiplayerState::~MultiplayerState() = default; |
| @@ -90,14 +89,7 @@ void MultiplayerState::Close() { | |||
| 90 | void MultiplayerState::retranslateUi() { | 89 | void MultiplayerState::retranslateUi() { |
| 91 | status_text->setToolTip(tr("Current connection status")); | 90 | status_text->setToolTip(tr("Current connection status")); |
| 92 | 91 | ||
| 93 | if (current_state == Network::RoomMember::State::Uninitialized) { | 92 | UpdateNotificationStatus(); |
| 94 | status_text->setText(tr("Not Connected. Click here to find a room!")); | ||
| 95 | } else if (current_state == Network::RoomMember::State::Joined || | ||
| 96 | current_state == Network::RoomMember::State::Moderator) { | ||
| 97 | status_text->setText(tr("Connected")); | ||
| 98 | } else { | ||
| 99 | status_text->setText(tr("Not Connected")); | ||
| 100 | } | ||
| 101 | 93 | ||
| 102 | if (lobby) { | 94 | if (lobby) { |
| 103 | lobby->RetranslateUi(); | 95 | lobby->RetranslateUi(); |
| @@ -113,21 +105,55 @@ void MultiplayerState::retranslateUi() { | |||
| 113 | } | 105 | } |
| 114 | } | 106 | } |
| 115 | 107 | ||
| 108 | void MultiplayerState::SetNotificationStatus(NotificationStatus status) { | ||
| 109 | notification_status = status; | ||
| 110 | UpdateNotificationStatus(); | ||
| 111 | } | ||
| 112 | |||
| 113 | void MultiplayerState::UpdateNotificationStatus() { | ||
| 114 | switch (notification_status) { | ||
| 115 | case NotificationStatus::Unitialized: | ||
| 116 | status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16)); | ||
| 117 | status_text->setText(tr("Not Connected. Click here to find a room!")); | ||
| 118 | leave_room->setEnabled(false); | ||
| 119 | show_room->setEnabled(false); | ||
| 120 | break; | ||
| 121 | case NotificationStatus::Disconnected: | ||
| 122 | status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16)); | ||
| 123 | status_text->setText(tr("Not Connected")); | ||
| 124 | leave_room->setEnabled(false); | ||
| 125 | show_room->setEnabled(false); | ||
| 126 | break; | ||
| 127 | case NotificationStatus::Connected: | ||
| 128 | status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected")).pixmap(16)); | ||
| 129 | status_text->setText(tr("Connected")); | ||
| 130 | leave_room->setEnabled(true); | ||
| 131 | show_room->setEnabled(true); | ||
| 132 | break; | ||
| 133 | case NotificationStatus::Notification: | ||
| 134 | status_icon->setPixmap( | ||
| 135 | QIcon::fromTheme(QStringLiteral("connected_notification")).pixmap(16)); | ||
| 136 | status_text->setText(tr("New Messages Received")); | ||
| 137 | leave_room->setEnabled(true); | ||
| 138 | show_room->setEnabled(true); | ||
| 139 | break; | ||
| 140 | } | ||
| 141 | |||
| 142 | // Clean up status bar if game is running | ||
| 143 | if (system.IsPoweredOn()) { | ||
| 144 | status_text->clear(); | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 116 | void MultiplayerState::OnNetworkStateChanged(const Network::RoomMember::State& state) { | 148 | void MultiplayerState::OnNetworkStateChanged(const Network::RoomMember::State& state) { |
| 117 | LOG_DEBUG(Frontend, "Network State: {}", Network::GetStateStr(state)); | 149 | LOG_DEBUG(Frontend, "Network State: {}", Network::GetStateStr(state)); |
| 118 | if (state == Network::RoomMember::State::Joined || | 150 | if (state == Network::RoomMember::State::Joined || |
| 119 | state == Network::RoomMember::State::Moderator) { | 151 | state == Network::RoomMember::State::Moderator) { |
| 120 | 152 | ||
| 121 | OnOpenNetworkRoom(); | 153 | OnOpenNetworkRoom(); |
| 122 | status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected")).pixmap(16)); | 154 | SetNotificationStatus(NotificationStatus::Connected); |
| 123 | status_text->setText(tr("Connected")); | ||
| 124 | leave_room->setEnabled(true); | ||
| 125 | show_room->setEnabled(true); | ||
| 126 | } else { | 155 | } else { |
| 127 | status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16)); | 156 | SetNotificationStatus(NotificationStatus::Disconnected); |
| 128 | status_text->setText(tr("Not Connected")); | ||
| 129 | leave_room->setEnabled(false); | ||
| 130 | show_room->setEnabled(false); | ||
| 131 | } | 157 | } |
| 132 | 158 | ||
| 133 | current_state = state; | 159 | current_state = state; |
| @@ -185,6 +211,10 @@ void MultiplayerState::OnAnnounceFailed(const WebService::WebResult& result) { | |||
| 185 | QMessageBox::Ok); | 211 | QMessageBox::Ok); |
| 186 | } | 212 | } |
| 187 | 213 | ||
| 214 | void MultiplayerState::OnSaveConfig() { | ||
| 215 | emit SaveConfig(); | ||
| 216 | } | ||
| 217 | |||
| 188 | void MultiplayerState::UpdateThemedIcons() { | 218 | void MultiplayerState::UpdateThemedIcons() { |
| 189 | if (show_notification) { | 219 | if (show_notification) { |
| 190 | status_icon->setPixmap( | 220 | status_icon->setPixmap( |
| @@ -209,13 +239,16 @@ static void BringWidgetToFront(QWidget* widget) { | |||
| 209 | void MultiplayerState::OnViewLobby() { | 239 | void MultiplayerState::OnViewLobby() { |
| 210 | if (lobby == nullptr) { | 240 | if (lobby == nullptr) { |
| 211 | lobby = new Lobby(this, game_list_model, announce_multiplayer_session, system); | 241 | lobby = new Lobby(this, game_list_model, announce_multiplayer_session, system); |
| 242 | connect(lobby, &Lobby::SaveConfig, this, &MultiplayerState::OnSaveConfig); | ||
| 212 | } | 243 | } |
| 244 | lobby->RefreshLobby(); | ||
| 213 | BringWidgetToFront(lobby); | 245 | BringWidgetToFront(lobby); |
| 214 | } | 246 | } |
| 215 | 247 | ||
| 216 | void MultiplayerState::OnCreateRoom() { | 248 | void MultiplayerState::OnCreateRoom() { |
| 217 | if (host_room == nullptr) { | 249 | if (host_room == nullptr) { |
| 218 | host_room = new HostRoomWindow(this, game_list_model, announce_multiplayer_session, system); | 250 | host_room = new HostRoomWindow(this, game_list_model, announce_multiplayer_session, system); |
| 251 | connect(host_room, &HostRoomWindow::SaveConfig, this, &MultiplayerState::OnSaveConfig); | ||
| 219 | } | 252 | } |
| 220 | BringWidgetToFront(host_room); | 253 | BringWidgetToFront(host_room); |
| 221 | } | 254 | } |
| @@ -249,14 +282,13 @@ void MultiplayerState::ShowNotification() { | |||
| 249 | return; // Do not show notification if the chat window currently has focus | 282 | return; // Do not show notification if the chat window currently has focus |
| 250 | show_notification = true; | 283 | show_notification = true; |
| 251 | QApplication::alert(nullptr); | 284 | QApplication::alert(nullptr); |
| 252 | status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected_notification")).pixmap(16)); | 285 | QApplication::beep(); |
| 253 | status_text->setText(tr("New Messages Received")); | 286 | SetNotificationStatus(NotificationStatus::Notification); |
| 254 | } | 287 | } |
| 255 | 288 | ||
| 256 | void MultiplayerState::HideNotification() { | 289 | void MultiplayerState::HideNotification() { |
| 257 | show_notification = false; | 290 | show_notification = false; |
| 258 | status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected")).pixmap(16)); | 291 | SetNotificationStatus(NotificationStatus::Connected); |
| 259 | status_text->setText(tr("Connected")); | ||
| 260 | } | 292 | } |
| 261 | 293 | ||
| 262 | void MultiplayerState::OnOpenNetworkRoom() { | 294 | void MultiplayerState::OnOpenNetworkRoom() { |
| @@ -279,6 +311,8 @@ void MultiplayerState::OnOpenNetworkRoom() { | |||
| 279 | void MultiplayerState::OnDirectConnectToRoom() { | 311 | void MultiplayerState::OnDirectConnectToRoom() { |
| 280 | if (direct_connect == nullptr) { | 312 | if (direct_connect == nullptr) { |
| 281 | direct_connect = new DirectConnectWindow(system, this); | 313 | direct_connect = new DirectConnectWindow(system, this); |
| 314 | connect(direct_connect, &DirectConnectWindow::SaveConfig, this, | ||
| 315 | &MultiplayerState::OnSaveConfig); | ||
| 282 | } | 316 | } |
| 283 | BringWidgetToFront(direct_connect); | 317 | BringWidgetToFront(direct_connect); |
| 284 | } | 318 | } |
diff --git a/src/yuzu/multiplayer/state.h b/src/yuzu/multiplayer/state.h index c92496413..5d681c5c6 100644 --- a/src/yuzu/multiplayer/state.h +++ b/src/yuzu/multiplayer/state.h | |||
| @@ -22,6 +22,13 @@ class MultiplayerState : public QWidget { | |||
| 22 | Q_OBJECT; | 22 | Q_OBJECT; |
| 23 | 23 | ||
| 24 | public: | 24 | public: |
| 25 | enum class NotificationStatus { | ||
| 26 | Unitialized, | ||
| 27 | Disconnected, | ||
| 28 | Connected, | ||
| 29 | Notification, | ||
| 30 | }; | ||
| 31 | |||
| 25 | explicit MultiplayerState(QWidget* parent, QStandardItemModel* game_list, QAction* leave_room, | 32 | explicit MultiplayerState(QWidget* parent, QStandardItemModel* game_list, QAction* leave_room, |
| 26 | QAction* show_room, Core::System& system_); | 33 | QAction* show_room, Core::System& system_); |
| 27 | ~MultiplayerState(); | 34 | ~MultiplayerState(); |
| @@ -31,6 +38,10 @@ public: | |||
| 31 | */ | 38 | */ |
| 32 | void Close(); | 39 | void Close(); |
| 33 | 40 | ||
| 41 | void SetNotificationStatus(NotificationStatus state); | ||
| 42 | |||
| 43 | void UpdateNotificationStatus(); | ||
| 44 | |||
| 34 | ClickableLabel* GetStatusText() const { | 45 | ClickableLabel* GetStatusText() const { |
| 35 | return status_text; | 46 | return status_text; |
| 36 | } | 47 | } |
| @@ -64,6 +75,7 @@ public slots: | |||
| 64 | void OnOpenNetworkRoom(); | 75 | void OnOpenNetworkRoom(); |
| 65 | void OnDirectConnectToRoom(); | 76 | void OnDirectConnectToRoom(); |
| 66 | void OnAnnounceFailed(const WebService::WebResult&); | 77 | void OnAnnounceFailed(const WebService::WebResult&); |
| 78 | void OnSaveConfig(); | ||
| 67 | void UpdateThemedIcons(); | 79 | void UpdateThemedIcons(); |
| 68 | void ShowNotification(); | 80 | void ShowNotification(); |
| 69 | void HideNotification(); | 81 | void HideNotification(); |
| @@ -72,6 +84,7 @@ signals: | |||
| 72 | void NetworkStateChanged(const Network::RoomMember::State&); | 84 | void NetworkStateChanged(const Network::RoomMember::State&); |
| 73 | void NetworkError(const Network::RoomMember::Error&); | 85 | void NetworkError(const Network::RoomMember::Error&); |
| 74 | void AnnounceFailed(const WebService::WebResult&); | 86 | void AnnounceFailed(const WebService::WebResult&); |
| 87 | void SaveConfig(); | ||
| 75 | 88 | ||
| 76 | private: | 89 | private: |
| 77 | Lobby* lobby = nullptr; | 90 | Lobby* lobby = nullptr; |
| @@ -85,6 +98,7 @@ private: | |||
| 85 | QAction* show_room; | 98 | QAction* show_room; |
| 86 | std::shared_ptr<Core::AnnounceMultiplayerSession> announce_multiplayer_session; | 99 | std::shared_ptr<Core::AnnounceMultiplayerSession> announce_multiplayer_session; |
| 87 | Network::RoomMember::State current_state = Network::RoomMember::State::Uninitialized; | 100 | Network::RoomMember::State current_state = Network::RoomMember::State::Uninitialized; |
| 101 | NotificationStatus notification_status = NotificationStatus::Unitialized; | ||
| 88 | bool has_mod_perms = false; | 102 | bool has_mod_perms = false; |
| 89 | Network::RoomMember::CallbackHandle<Network::RoomMember::State> state_callback_handle; | 103 | Network::RoomMember::CallbackHandle<Network::RoomMember::State> state_callback_handle; |
| 90 | Network::RoomMember::CallbackHandle<Network::RoomMember::Error> error_callback_handle; | 104 | Network::RoomMember::CallbackHandle<Network::RoomMember::Error> error_callback_handle; |
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index e12d414d9..753797efc 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h | |||
| @@ -102,7 +102,7 @@ struct Values { | |||
| 102 | Settings::Setting<uint32_t> callout_flags{0, "calloutFlags"}; | 102 | Settings::Setting<uint32_t> callout_flags{0, "calloutFlags"}; |
| 103 | 103 | ||
| 104 | // multiplayer settings | 104 | // multiplayer settings |
| 105 | Settings::Setting<QString> multiplayer_nickname{QStringLiteral("yuzu"), "nickname"}; | 105 | Settings::Setting<QString> multiplayer_nickname{{}, "nickname"}; |
| 106 | Settings::Setting<QString> multiplayer_ip{{}, "ip"}; | 106 | Settings::Setting<QString> multiplayer_ip{{}, "ip"}; |
| 107 | Settings::SwitchableSetting<uint, true> multiplayer_port{24872, 0, UINT16_MAX, "port"}; | 107 | Settings::SwitchableSetting<uint, true> multiplayer_port{24872, 0, UINT16_MAX, "port"}; |
| 108 | Settings::Setting<QString> multiplayer_room_nickname{{}, "room_nickname"}; | 108 | Settings::Setting<QString> multiplayer_room_nickname{{}, "room_nickname"}; |