diff options
37 files changed, 1459 insertions, 539 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fc177fa52..54de1dc94 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt | |||
| @@ -162,6 +162,7 @@ add_subdirectory(video_core) | |||
| 162 | add_subdirectory(network) | 162 | add_subdirectory(network) |
| 163 | add_subdirectory(input_common) | 163 | add_subdirectory(input_common) |
| 164 | add_subdirectory(shader_recompiler) | 164 | add_subdirectory(shader_recompiler) |
| 165 | add_subdirectory(dedicated_room) | ||
| 165 | 166 | ||
| 166 | if (YUZU_TESTS) | 167 | if (YUZU_TESTS) |
| 167 | add_subdirectory(tests) | 168 | add_subdirectory(tests) |
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index a6dc31b53..635fb85c8 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -124,6 +124,7 @@ add_library(common STATIC | |||
| 124 | settings.h | 124 | settings.h |
| 125 | settings_input.cpp | 125 | settings_input.cpp |
| 126 | settings_input.h | 126 | settings_input.h |
| 127 | socket_types.h | ||
| 127 | spin_lock.cpp | 128 | spin_lock.cpp |
| 128 | spin_lock.h | 129 | spin_lock.h |
| 129 | stream.cpp | 130 | stream.cpp |
diff --git a/src/common/announce_multiplayer_room.h b/src/common/announce_multiplayer_room.h index 0ad9da2be..cb004e0eb 100644 --- a/src/common/announce_multiplayer_room.h +++ b/src/common/announce_multiplayer_room.h | |||
| @@ -8,12 +8,11 @@ | |||
| 8 | #include <string> | 8 | #include <string> |
| 9 | #include <vector> | 9 | #include <vector> |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/socket_types.h" | ||
| 11 | #include "web_service/web_result.h" | 12 | #include "web_service/web_result.h" |
| 12 | 13 | ||
| 13 | namespace AnnounceMultiplayerRoom { | 14 | namespace AnnounceMultiplayerRoom { |
| 14 | 15 | ||
| 15 | using MacAddress = std::array<u8, 6>; | ||
| 16 | |||
| 17 | struct GameInfo { | 16 | struct GameInfo { |
| 18 | std::string name{""}; | 17 | std::string name{""}; |
| 19 | u64 id{0}; | 18 | u64 id{0}; |
| @@ -24,7 +23,7 @@ struct Member { | |||
| 24 | std::string nickname; | 23 | std::string nickname; |
| 25 | std::string display_name; | 24 | std::string display_name; |
| 26 | std::string avatar_url; | 25 | std::string avatar_url; |
| 27 | MacAddress mac_address; | 26 | Network::IPv4Address fake_ip; |
| 28 | GameInfo game; | 27 | GameInfo game; |
| 29 | }; | 28 | }; |
| 30 | 29 | ||
| @@ -75,10 +74,7 @@ public: | |||
| 75 | const bool has_password, const GameInfo& preferred_game) = 0; | 74 | const bool has_password, const GameInfo& preferred_game) = 0; |
| 76 | /** | 75 | /** |
| 77 | * Adds a player information to the data that gets announced | 76 | * Adds a player information to the data that gets announced |
| 78 | * @param nickname The nickname of the player | 77 | * @param member The player to add |
| 79 | * @param mac_address The MAC Address of the player | ||
| 80 | * @param game_id The title id of the game the player plays | ||
| 81 | * @param game_name The name of the game the player plays | ||
| 82 | */ | 78 | */ |
| 83 | virtual void AddPlayer(const Member& member) = 0; | 79 | virtual void AddPlayer(const Member& member) = 0; |
| 84 | 80 | ||
diff --git a/src/common/socket_types.h b/src/common/socket_types.h new file mode 100644 index 000000000..0a801a443 --- /dev/null +++ b/src/common/socket_types.h | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/common_types.h" | ||
| 7 | |||
| 8 | namespace Network { | ||
| 9 | |||
| 10 | /// Address families | ||
| 11 | enum class Domain : u8 { | ||
| 12 | INET, ///< Address family for IPv4 | ||
| 13 | }; | ||
| 14 | |||
| 15 | /// Socket types | ||
| 16 | enum class Type { | ||
| 17 | STREAM, | ||
| 18 | DGRAM, | ||
| 19 | RAW, | ||
| 20 | SEQPACKET, | ||
| 21 | }; | ||
| 22 | |||
| 23 | /// Protocol values for sockets | ||
| 24 | enum class Protocol : u8 { | ||
| 25 | ICMP, | ||
| 26 | TCP, | ||
| 27 | UDP, | ||
| 28 | }; | ||
| 29 | |||
| 30 | /// Shutdown mode | ||
| 31 | enum class ShutdownHow { | ||
| 32 | RD, | ||
| 33 | WR, | ||
| 34 | RDWR, | ||
| 35 | }; | ||
| 36 | |||
| 37 | /// Array of IPv4 address | ||
| 38 | using IPv4Address = std::array<u8, 4>; | ||
| 39 | |||
| 40 | /// Cross-platform sockaddr structure | ||
| 41 | struct SockAddrIn { | ||
| 42 | Domain family; | ||
| 43 | IPv4Address ip; | ||
| 44 | u16 portno; | ||
| 45 | }; | ||
| 46 | |||
| 47 | constexpr u32 FLAG_MSG_PEEK = 0x2; | ||
| 48 | constexpr u32 FLAG_MSG_DONTWAIT = 0x80; | ||
| 49 | constexpr u32 FLAG_O_NONBLOCK = 0x800; | ||
| 50 | |||
| 51 | } // namespace Network | ||
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 4e39649a8..3230d7199 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -724,6 +724,8 @@ add_library(core STATIC | |||
| 724 | internal_network/network_interface.cpp | 724 | internal_network/network_interface.cpp |
| 725 | internal_network/network_interface.h | 725 | internal_network/network_interface.h |
| 726 | internal_network/sockets.h | 726 | internal_network/sockets.h |
| 727 | internal_network/socket_proxy.cpp | ||
| 728 | internal_network/socket_proxy.h | ||
| 727 | loader/deconstructed_rom_directory.cpp | 729 | loader/deconstructed_rom_directory.cpp |
| 728 | loader/deconstructed_rom_directory.h | 730 | loader/deconstructed_rom_directory.h |
| 729 | loader/kip.cpp | 731 | loader/kip.cpp |
diff --git a/src/core/announce_multiplayer_session.cpp b/src/core/announce_multiplayer_session.cpp index d73a488cf..6737ce85a 100644 --- a/src/core/announce_multiplayer_session.cpp +++ b/src/core/announce_multiplayer_session.cpp | |||
| @@ -31,7 +31,7 @@ AnnounceMultiplayerSession::AnnounceMultiplayerSession(Network::RoomNetwork& roo | |||
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | WebService::WebResult AnnounceMultiplayerSession::Register() { | 33 | WebService::WebResult AnnounceMultiplayerSession::Register() { |
| 34 | std::shared_ptr<Network::Room> room = room_network.GetRoom().lock(); | 34 | auto room = room_network.GetRoom().lock(); |
| 35 | if (!room) { | 35 | if (!room) { |
| 36 | return WebService::WebResult{WebService::WebResult::Code::LibError, | 36 | return WebService::WebResult{WebService::WebResult::Code::LibError, |
| 37 | "Network is not initialized", ""}; | 37 | "Network is not initialized", ""}; |
| @@ -102,7 +102,7 @@ void AnnounceMultiplayerSession::UpdateBackendData(std::shared_ptr<Network::Room | |||
| 102 | void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() { | 102 | void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() { |
| 103 | // Invokes all current bound error callbacks. | 103 | // Invokes all current bound error callbacks. |
| 104 | const auto ErrorCallback = [this](WebService::WebResult result) { | 104 | const auto ErrorCallback = [this](WebService::WebResult result) { |
| 105 | std::lock_guard<std::mutex> lock(callback_mutex); | 105 | std::lock_guard lock(callback_mutex); |
| 106 | for (auto callback : error_callbacks) { | 106 | for (auto callback : error_callbacks) { |
| 107 | (*callback)(result); | 107 | (*callback)(result); |
| 108 | } | 108 | } |
| @@ -120,7 +120,7 @@ void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() { | |||
| 120 | std::future<WebService::WebResult> future; | 120 | std::future<WebService::WebResult> future; |
| 121 | while (!shutdown_event.WaitUntil(update_time)) { | 121 | while (!shutdown_event.WaitUntil(update_time)) { |
| 122 | update_time += announce_time_interval; | 122 | update_time += announce_time_interval; |
| 123 | std::shared_ptr<Network::Room> room = room_network.GetRoom().lock(); | 123 | auto room = room_network.GetRoom().lock(); |
| 124 | if (!room) { | 124 | if (!room) { |
| 125 | break; | 125 | break; |
| 126 | } | 126 | } |
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 2889973e4..e3ef06481 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp | |||
| @@ -6,7 +6,6 @@ | |||
| 6 | #include "core/hle/kernel/k_event.h" | 6 | #include "core/hle/kernel/k_event.h" |
| 7 | #include "core/hle/service/kernel_helpers.h" | 7 | #include "core/hle/service/kernel_helpers.h" |
| 8 | #include "core/hle/service/nifm/nifm.h" | 8 | #include "core/hle/service/nifm/nifm.h" |
| 9 | #include "core/hle/service/service.h" | ||
| 10 | 9 | ||
| 11 | namespace { | 10 | namespace { |
| 12 | 11 | ||
| @@ -271,213 +270,228 @@ public: | |||
| 271 | } | 270 | } |
| 272 | }; | 271 | }; |
| 273 | 272 | ||
| 274 | class IGeneralService final : public ServiceFramework<IGeneralService> { | 273 | void IGeneralService::GetClientId(Kernel::HLERequestContext& ctx) { |
| 275 | public: | 274 | static constexpr u32 client_id = 1; |
| 276 | explicit IGeneralService(Core::System& system_); | 275 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); |
| 277 | 276 | ||
| 278 | private: | 277 | IPC::ResponseBuilder rb{ctx, 4}; |
| 279 | void GetClientId(Kernel::HLERequestContext& ctx) { | 278 | rb.Push(ResultSuccess); |
| 280 | static constexpr u32 client_id = 1; | 279 | rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid |
| 281 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | 280 | } |
| 282 | 281 | ||
| 283 | IPC::ResponseBuilder rb{ctx, 4}; | 282 | void IGeneralService::CreateScanRequest(Kernel::HLERequestContext& ctx) { |
| 284 | rb.Push(ResultSuccess); | 283 | LOG_DEBUG(Service_NIFM, "called"); |
| 285 | rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid | ||
| 286 | } | ||
| 287 | 284 | ||
| 288 | void CreateScanRequest(Kernel::HLERequestContext& ctx) { | 285 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 289 | LOG_DEBUG(Service_NIFM, "called"); | ||
| 290 | 286 | ||
| 291 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 287 | rb.Push(ResultSuccess); |
| 288 | rb.PushIpcInterface<IScanRequest>(system); | ||
| 289 | } | ||
| 292 | 290 | ||
| 293 | rb.Push(ResultSuccess); | 291 | void IGeneralService::CreateRequest(Kernel::HLERequestContext& ctx) { |
| 294 | rb.PushIpcInterface<IScanRequest>(system); | 292 | LOG_DEBUG(Service_NIFM, "called"); |
| 295 | } | ||
| 296 | 293 | ||
| 297 | void CreateRequest(Kernel::HLERequestContext& ctx) { | 294 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 298 | LOG_DEBUG(Service_NIFM, "called"); | ||
| 299 | 295 | ||
| 300 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 296 | rb.Push(ResultSuccess); |
| 297 | rb.PushIpcInterface<IRequest>(system); | ||
| 298 | } | ||
| 301 | 299 | ||
| 302 | rb.Push(ResultSuccess); | 300 | void IGeneralService::GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) { |
| 303 | rb.PushIpcInterface<IRequest>(system); | 301 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); |
| 304 | } | ||
| 305 | 302 | ||
| 306 | void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) { | 303 | const auto net_iface = Network::GetSelectedNetworkInterface(); |
| 307 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | ||
| 308 | 304 | ||
| 309 | const auto net_iface = Network::GetSelectedNetworkInterface(); | 305 | SfNetworkProfileData network_profile_data = [&net_iface] { |
| 310 | 306 | if (!net_iface) { | |
| 311 | const SfNetworkProfileData network_profile_data = [&net_iface] { | 307 | return SfNetworkProfileData{}; |
| 312 | if (!net_iface) { | 308 | } |
| 313 | return SfNetworkProfileData{}; | 309 | |
| 314 | } | 310 | return SfNetworkProfileData{ |
| 315 | 311 | .ip_setting_data{ | |
| 316 | return SfNetworkProfileData{ | 312 | .ip_address_setting{ |
| 317 | .ip_setting_data{ | 313 | .is_automatic{true}, |
| 318 | .ip_address_setting{ | 314 | .current_address{Network::TranslateIPv4(net_iface->ip_address)}, |
| 319 | .is_automatic{true}, | 315 | .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)}, |
| 320 | .current_address{Network::TranslateIPv4(net_iface->ip_address)}, | 316 | .gateway{Network::TranslateIPv4(net_iface->gateway)}, |
| 321 | .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)}, | ||
| 322 | .gateway{Network::TranslateIPv4(net_iface->gateway)}, | ||
| 323 | }, | ||
| 324 | .dns_setting{ | ||
| 325 | .is_automatic{true}, | ||
| 326 | .primary_dns{1, 1, 1, 1}, | ||
| 327 | .secondary_dns{1, 0, 0, 1}, | ||
| 328 | }, | ||
| 329 | .proxy_setting{ | ||
| 330 | .enabled{false}, | ||
| 331 | .port{}, | ||
| 332 | .proxy_server{}, | ||
| 333 | .automatic_auth_enabled{}, | ||
| 334 | .user{}, | ||
| 335 | .password{}, | ||
| 336 | }, | ||
| 337 | .mtu{1500}, | ||
| 338 | }, | 317 | }, |
| 339 | .uuid{0xdeadbeef, 0xdeadbeef}, | 318 | .dns_setting{ |
| 340 | .network_name{"yuzu Network"}, | 319 | .is_automatic{true}, |
| 341 | .wireless_setting_data{ | 320 | .primary_dns{1, 1, 1, 1}, |
| 342 | .ssid_length{12}, | 321 | .secondary_dns{1, 0, 0, 1}, |
| 343 | .ssid{"yuzu Network"}, | ||
| 344 | .passphrase{"yuzupassword"}, | ||
| 345 | }, | 322 | }, |
| 346 | }; | 323 | .proxy_setting{ |
| 347 | }(); | 324 | .enabled{false}, |
| 348 | 325 | .port{}, | |
| 349 | ctx.WriteBuffer(network_profile_data); | 326 | .proxy_server{}, |
| 327 | .automatic_auth_enabled{}, | ||
| 328 | .user{}, | ||
| 329 | .password{}, | ||
| 330 | }, | ||
| 331 | .mtu{1500}, | ||
| 332 | }, | ||
| 333 | .uuid{0xdeadbeef, 0xdeadbeef}, | ||
| 334 | .network_name{"yuzu Network"}, | ||
| 335 | .wireless_setting_data{ | ||
| 336 | .ssid_length{12}, | ||
| 337 | .ssid{"yuzu Network"}, | ||
| 338 | .passphrase{"yuzupassword"}, | ||
| 339 | }, | ||
| 340 | }; | ||
| 341 | }(); | ||
| 350 | 342 | ||
| 351 | IPC::ResponseBuilder rb{ctx, 2}; | 343 | // When we're connected to a room, spoof the hosts IP address |
| 352 | rb.Push(ResultSuccess); | 344 | if (auto room_member = network.GetRoomMember().lock()) { |
| 345 | if (room_member->IsConnected()) { | ||
| 346 | network_profile_data.ip_setting_data.ip_address_setting.current_address = | ||
| 347 | room_member->GetFakeIpAddress(); | ||
| 348 | } | ||
| 353 | } | 349 | } |
| 354 | 350 | ||
| 355 | void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { | 351 | ctx.WriteBuffer(network_profile_data); |
| 356 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | ||
| 357 | 352 | ||
| 358 | IPC::ResponseBuilder rb{ctx, 2}; | 353 | IPC::ResponseBuilder rb{ctx, 2}; |
| 359 | rb.Push(ResultSuccess); | 354 | rb.Push(ResultSuccess); |
| 360 | } | 355 | } |
| 361 | 356 | ||
| 362 | void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { | 357 | void IGeneralService::RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { |
| 363 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | 358 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); |
| 364 | 359 | ||
| 365 | auto ipv4 = Network::GetHostIPv4Address(); | 360 | IPC::ResponseBuilder rb{ctx, 2}; |
| 366 | if (!ipv4) { | 361 | rb.Push(ResultSuccess); |
| 367 | LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0"); | 362 | } |
| 368 | ipv4.emplace(Network::IPv4Address{0, 0, 0, 0}); | ||
| 369 | } | ||
| 370 | 363 | ||
| 371 | IPC::ResponseBuilder rb{ctx, 3}; | 364 | void IGeneralService::GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { |
| 372 | rb.Push(ResultSuccess); | 365 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); |
| 373 | rb.PushRaw(*ipv4); | 366 | |
| 367 | auto ipv4 = Network::GetHostIPv4Address(); | ||
| 368 | if (!ipv4) { | ||
| 369 | LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0"); | ||
| 370 | ipv4.emplace(Network::IPv4Address{0, 0, 0, 0}); | ||
| 374 | } | 371 | } |
| 375 | 372 | ||
| 376 | void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { | 373 | // When we're connected to a room, spoof the hosts IP address |
| 377 | LOG_DEBUG(Service_NIFM, "called"); | 374 | if (auto room_member = network.GetRoomMember().lock()) { |
| 375 | if (room_member->IsConnected()) { | ||
| 376 | ipv4 = room_member->GetFakeIpAddress(); | ||
| 377 | } | ||
| 378 | } | ||
| 378 | 379 | ||
| 379 | ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, | 380 | IPC::ResponseBuilder rb{ctx, 3}; |
| 380 | "SfNetworkProfileData is not the correct size"); | 381 | rb.Push(ResultSuccess); |
| 381 | u128 uuid{}; | 382 | rb.PushRaw(*ipv4); |
| 382 | auto buffer = ctx.ReadBuffer(); | 383 | } |
| 383 | std::memcpy(&uuid, buffer.data() + 8, sizeof(u128)); | ||
| 384 | 384 | ||
| 385 | IPC::ResponseBuilder rb{ctx, 6, 0, 1}; | 385 | void IGeneralService::CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { |
| 386 | LOG_DEBUG(Service_NIFM, "called"); | ||
| 386 | 387 | ||
| 387 | rb.Push(ResultSuccess); | 388 | ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, "SfNetworkProfileData is not the correct size"); |
| 388 | rb.PushIpcInterface<INetworkProfile>(system); | 389 | u128 uuid{}; |
| 389 | rb.PushRaw<u128>(uuid); | 390 | auto buffer = ctx.ReadBuffer(); |
| 390 | } | 391 | std::memcpy(&uuid, buffer.data() + 8, sizeof(u128)); |
| 391 | 392 | ||
| 392 | void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) { | 393 | IPC::ResponseBuilder rb{ctx, 6, 0, 1}; |
| 393 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | ||
| 394 | 394 | ||
| 395 | struct IpConfigInfo { | 395 | rb.Push(ResultSuccess); |
| 396 | IpAddressSetting ip_address_setting{}; | 396 | rb.PushIpcInterface<INetworkProfile>(system); |
| 397 | DnsSetting dns_setting{}; | 397 | rb.PushRaw<u128>(uuid); |
| 398 | }; | 398 | } |
| 399 | static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting), | ||
| 400 | "IpConfigInfo has incorrect size."); | ||
| 401 | 399 | ||
| 402 | const auto net_iface = Network::GetSelectedNetworkInterface(); | 400 | void IGeneralService::GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) { |
| 401 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | ||
| 403 | 402 | ||
| 404 | const IpConfigInfo ip_config_info = [&net_iface] { | 403 | struct IpConfigInfo { |
| 405 | if (!net_iface) { | 404 | IpAddressSetting ip_address_setting{}; |
| 406 | return IpConfigInfo{}; | 405 | DnsSetting dns_setting{}; |
| 407 | } | 406 | }; |
| 407 | static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting), | ||
| 408 | "IpConfigInfo has incorrect size."); | ||
| 408 | 409 | ||
| 409 | return IpConfigInfo{ | 410 | const auto net_iface = Network::GetSelectedNetworkInterface(); |
| 410 | .ip_address_setting{ | ||
| 411 | .is_automatic{true}, | ||
| 412 | .current_address{Network::TranslateIPv4(net_iface->ip_address)}, | ||
| 413 | .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)}, | ||
| 414 | .gateway{Network::TranslateIPv4(net_iface->gateway)}, | ||
| 415 | }, | ||
| 416 | .dns_setting{ | ||
| 417 | .is_automatic{true}, | ||
| 418 | .primary_dns{1, 1, 1, 1}, | ||
| 419 | .secondary_dns{1, 0, 0, 1}, | ||
| 420 | }, | ||
| 421 | }; | ||
| 422 | }(); | ||
| 423 | 411 | ||
| 424 | IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)}; | 412 | IpConfigInfo ip_config_info = [&net_iface] { |
| 425 | rb.Push(ResultSuccess); | 413 | if (!net_iface) { |
| 426 | rb.PushRaw<IpConfigInfo>(ip_config_info); | 414 | return IpConfigInfo{}; |
| 427 | } | 415 | } |
| 428 | 416 | ||
| 429 | void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) { | 417 | return IpConfigInfo{ |
| 430 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | 418 | .ip_address_setting{ |
| 419 | .is_automatic{true}, | ||
| 420 | .current_address{Network::TranslateIPv4(net_iface->ip_address)}, | ||
| 421 | .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)}, | ||
| 422 | .gateway{Network::TranslateIPv4(net_iface->gateway)}, | ||
| 423 | }, | ||
| 424 | .dns_setting{ | ||
| 425 | .is_automatic{true}, | ||
| 426 | .primary_dns{1, 1, 1, 1}, | ||
| 427 | .secondary_dns{1, 0, 0, 1}, | ||
| 428 | }, | ||
| 429 | }; | ||
| 430 | }(); | ||
| 431 | 431 | ||
| 432 | IPC::ResponseBuilder rb{ctx, 3}; | 432 | // When we're connected to a room, spoof the hosts IP address |
| 433 | rb.Push(ResultSuccess); | 433 | if (auto room_member = network.GetRoomMember().lock()) { |
| 434 | rb.Push<u8>(0); | 434 | if (room_member->IsConnected()) { |
| 435 | ip_config_info.ip_address_setting.current_address = room_member->GetFakeIpAddress(); | ||
| 436 | } | ||
| 435 | } | 437 | } |
| 436 | 438 | ||
| 437 | void GetInternetConnectionStatus(Kernel::HLERequestContext& ctx) { | 439 | IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)}; |
| 438 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | 440 | rb.Push(ResultSuccess); |
| 441 | rb.PushRaw<IpConfigInfo>(ip_config_info); | ||
| 442 | } | ||
| 439 | 443 | ||
| 440 | struct Output { | 444 | void IGeneralService::IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) { |
| 441 | InternetConnectionType type{InternetConnectionType::WiFi}; | 445 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); |
| 442 | u8 wifi_strength{3}; | ||
| 443 | InternetConnectionStatus state{InternetConnectionStatus::Connected}; | ||
| 444 | }; | ||
| 445 | static_assert(sizeof(Output) == 0x3, "Output has incorrect size."); | ||
| 446 | 446 | ||
| 447 | constexpr Output out{}; | 447 | IPC::ResponseBuilder rb{ctx, 3}; |
| 448 | rb.Push(ResultSuccess); | ||
| 449 | rb.Push<u8>(1); | ||
| 450 | } | ||
| 448 | 451 | ||
| 449 | IPC::ResponseBuilder rb{ctx, 3}; | 452 | void IGeneralService::GetInternetConnectionStatus(Kernel::HLERequestContext& ctx) { |
| 450 | rb.Push(ResultSuccess); | 453 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); |
| 451 | rb.PushRaw(out); | ||
| 452 | } | ||
| 453 | 454 | ||
| 454 | void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) { | 455 | struct Output { |
| 455 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | 456 | InternetConnectionType type{InternetConnectionType::WiFi}; |
| 457 | u8 wifi_strength{3}; | ||
| 458 | InternetConnectionStatus state{InternetConnectionStatus::Connected}; | ||
| 459 | }; | ||
| 460 | static_assert(sizeof(Output) == 0x3, "Output has incorrect size."); | ||
| 456 | 461 | ||
| 457 | IPC::ResponseBuilder rb{ctx, 3}; | 462 | constexpr Output out{}; |
| 458 | rb.Push(ResultSuccess); | 463 | |
| 459 | if (Network::GetHostIPv4Address().has_value()) { | 464 | IPC::ResponseBuilder rb{ctx, 3}; |
| 460 | rb.Push<u8>(1); | 465 | rb.Push(ResultSuccess); |
| 461 | } else { | 466 | rb.PushRaw(out); |
| 462 | rb.Push<u8>(0); | 467 | } |
| 463 | } | 468 | |
| 469 | void IGeneralService::IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) { | ||
| 470 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | ||
| 471 | |||
| 472 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 473 | rb.Push(ResultSuccess); | ||
| 474 | if (Network::GetHostIPv4Address().has_value()) { | ||
| 475 | rb.Push<u8>(1); | ||
| 476 | } else { | ||
| 477 | rb.Push<u8>(0); | ||
| 464 | } | 478 | } |
| 479 | } | ||
| 465 | 480 | ||
| 466 | void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { | 481 | void IGeneralService::IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { |
| 467 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | 482 | LOG_ERROR(Service_NIFM, "(STUBBED) called"); |
| 468 | 483 | ||
| 469 | IPC::ResponseBuilder rb{ctx, 3}; | 484 | IPC::ResponseBuilder rb{ctx, 3}; |
| 470 | rb.Push(ResultSuccess); | 485 | rb.Push(ResultSuccess); |
| 471 | if (Network::GetHostIPv4Address().has_value()) { | 486 | if (Network::GetHostIPv4Address().has_value()) { |
| 472 | rb.Push<u8>(1); | 487 | rb.Push<u8>(1); |
| 473 | } else { | 488 | } else { |
| 474 | rb.Push<u8>(0); | 489 | rb.Push<u8>(0); |
| 475 | } | ||
| 476 | } | 490 | } |
| 477 | }; | 491 | } |
| 478 | 492 | ||
| 479 | IGeneralService::IGeneralService(Core::System& system_) | 493 | IGeneralService::IGeneralService(Core::System& system_) |
| 480 | : ServiceFramework{system_, "IGeneralService"} { | 494 | : ServiceFramework{system_, "IGeneralService"}, network{system_.GetRoomNetwork()} { |
| 481 | // clang-format off | 495 | // clang-format off |
| 482 | static const FunctionInfo functions[] = { | 496 | static const FunctionInfo functions[] = { |
| 483 | {1, &IGeneralService::GetClientId, "GetClientId"}, | 497 | {1, &IGeneralService::GetClientId, "GetClientId"}, |
| @@ -528,6 +542,8 @@ IGeneralService::IGeneralService(Core::System& system_) | |||
| 528 | RegisterHandlers(functions); | 542 | RegisterHandlers(functions); |
| 529 | } | 543 | } |
| 530 | 544 | ||
| 545 | IGeneralService::~IGeneralService() = default; | ||
| 546 | |||
| 531 | class NetworkInterface final : public ServiceFramework<NetworkInterface> { | 547 | class NetworkInterface final : public ServiceFramework<NetworkInterface> { |
| 532 | public: | 548 | public: |
| 533 | explicit NetworkInterface(const char* name, Core::System& system_) | 549 | explicit NetworkInterface(const char* name, Core::System& system_) |
diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h index 5f62d0014..48161be28 100644 --- a/src/core/hle/service/nifm/nifm.h +++ b/src/core/hle/service/nifm/nifm.h | |||
| @@ -3,6 +3,11 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "core/hle/service/service.h" | ||
| 7 | #include "network/network.h" | ||
| 8 | #include "network/room.h" | ||
| 9 | #include "network/room_member.h" | ||
| 10 | |||
| 6 | namespace Core { | 11 | namespace Core { |
| 7 | class System; | 12 | class System; |
| 8 | } | 13 | } |
| @@ -16,4 +21,26 @@ namespace Service::NIFM { | |||
| 16 | /// Registers all NIFM services with the specified service manager. | 21 | /// Registers all NIFM services with the specified service manager. |
| 17 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); | 22 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); |
| 18 | 23 | ||
| 24 | class IGeneralService final : public ServiceFramework<IGeneralService> { | ||
| 25 | public: | ||
| 26 | explicit IGeneralService(Core::System& system_); | ||
| 27 | ~IGeneralService() override; | ||
| 28 | |||
| 29 | private: | ||
| 30 | void GetClientId(Kernel::HLERequestContext& ctx); | ||
| 31 | void CreateScanRequest(Kernel::HLERequestContext& ctx); | ||
| 32 | void CreateRequest(Kernel::HLERequestContext& ctx); | ||
| 33 | void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx); | ||
| 34 | void RemoveNetworkProfile(Kernel::HLERequestContext& ctx); | ||
| 35 | void GetCurrentIpAddress(Kernel::HLERequestContext& ctx); | ||
| 36 | void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx); | ||
| 37 | void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx); | ||
| 38 | void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx); | ||
| 39 | void GetInternetConnectionStatus(Kernel::HLERequestContext& ctx); | ||
| 40 | void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx); | ||
| 41 | void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx); | ||
| 42 | |||
| 43 | Network::RoomNetwork& network; | ||
| 44 | }; | ||
| 45 | |||
| 19 | } // namespace Service::NIFM | 46 | } // namespace Service::NIFM |
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index c7194731e..e08c3cb67 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp | |||
| @@ -9,12 +9,16 @@ | |||
| 9 | #include <fmt/format.h> | 9 | #include <fmt/format.h> |
| 10 | 10 | ||
| 11 | #include "common/microprofile.h" | 11 | #include "common/microprofile.h" |
| 12 | #include "common/socket_types.h" | ||
| 13 | #include "core/core.h" | ||
| 12 | #include "core/hle/ipc_helpers.h" | 14 | #include "core/hle/ipc_helpers.h" |
| 13 | #include "core/hle/kernel/k_thread.h" | 15 | #include "core/hle/kernel/k_thread.h" |
| 14 | #include "core/hle/service/sockets/bsd.h" | 16 | #include "core/hle/service/sockets/bsd.h" |
| 15 | #include "core/hle/service/sockets/sockets_translate.h" | 17 | #include "core/hle/service/sockets/sockets_translate.h" |
| 16 | #include "core/internal_network/network.h" | 18 | #include "core/internal_network/network.h" |
| 19 | #include "core/internal_network/socket_proxy.h" | ||
| 17 | #include "core/internal_network/sockets.h" | 20 | #include "core/internal_network/sockets.h" |
| 21 | #include "network/network.h" | ||
| 18 | 22 | ||
| 19 | namespace Service::Sockets { | 23 | namespace Service::Sockets { |
| 20 | 24 | ||
| @@ -472,7 +476,13 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco | |||
| 472 | 476 | ||
| 473 | LOG_INFO(Service, "New socket fd={}", fd); | 477 | LOG_INFO(Service, "New socket fd={}", fd); |
| 474 | 478 | ||
| 475 | descriptor.socket = std::make_unique<Network::Socket>(); | 479 | auto room_member = room_network.GetRoomMember().lock(); |
| 480 | if (room_member && room_member->IsConnected()) { | ||
| 481 | descriptor.socket = std::make_unique<Network::ProxySocket>(room_network); | ||
| 482 | } else { | ||
| 483 | descriptor.socket = std::make_unique<Network::Socket>(); | ||
| 484 | } | ||
| 485 | |||
| 476 | descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(type, protocol)); | 486 | descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(type, protocol)); |
| 477 | descriptor.is_connection_based = IsConnectionBased(type); | 487 | descriptor.is_connection_based = IsConnectionBased(type); |
| 478 | 488 | ||
| @@ -648,7 +658,7 @@ std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) { | |||
| 648 | ASSERT(arg == 0); | 658 | ASSERT(arg == 0); |
| 649 | return {descriptor.flags, Errno::SUCCESS}; | 659 | return {descriptor.flags, Errno::SUCCESS}; |
| 650 | case FcntlCmd::SETFL: { | 660 | case FcntlCmd::SETFL: { |
| 651 | const bool enable = (arg & FLAG_O_NONBLOCK) != 0; | 661 | const bool enable = (arg & Network::FLAG_O_NONBLOCK) != 0; |
| 652 | const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable)); | 662 | const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable)); |
| 653 | if (bsd_errno != Errno::SUCCESS) { | 663 | if (bsd_errno != Errno::SUCCESS) { |
| 654 | return {-1, bsd_errno}; | 664 | return {-1, bsd_errno}; |
| @@ -669,7 +679,7 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con | |||
| 669 | return Errno::BADF; | 679 | return Errno::BADF; |
| 670 | } | 680 | } |
| 671 | 681 | ||
| 672 | Network::Socket* const socket = file_descriptors[fd]->socket.get(); | 682 | Network::SocketBase* const socket = file_descriptors[fd]->socket.get(); |
| 673 | 683 | ||
| 674 | if (optname == OptName::LINGER) { | 684 | if (optname == OptName::LINGER) { |
| 675 | ASSERT(optlen == sizeof(Linger)); | 685 | ASSERT(optlen == sizeof(Linger)); |
| @@ -724,6 +734,8 @@ std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) | |||
| 724 | FileDescriptor& descriptor = *file_descriptors[fd]; | 734 | FileDescriptor& descriptor = *file_descriptors[fd]; |
| 725 | 735 | ||
| 726 | // Apply flags | 736 | // Apply flags |
| 737 | using Network::FLAG_MSG_DONTWAIT; | ||
| 738 | using Network::FLAG_O_NONBLOCK; | ||
| 727 | if ((flags & FLAG_MSG_DONTWAIT) != 0) { | 739 | if ((flags & FLAG_MSG_DONTWAIT) != 0) { |
| 728 | flags &= ~FLAG_MSG_DONTWAIT; | 740 | flags &= ~FLAG_MSG_DONTWAIT; |
| 729 | if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { | 741 | if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { |
| @@ -759,6 +771,8 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess | |||
| 759 | } | 771 | } |
| 760 | 772 | ||
| 761 | // Apply flags | 773 | // Apply flags |
| 774 | using Network::FLAG_MSG_DONTWAIT; | ||
| 775 | using Network::FLAG_O_NONBLOCK; | ||
| 762 | if ((flags & FLAG_MSG_DONTWAIT) != 0) { | 776 | if ((flags & FLAG_MSG_DONTWAIT) != 0) { |
| 763 | flags &= ~FLAG_MSG_DONTWAIT; | 777 | flags &= ~FLAG_MSG_DONTWAIT; |
| 764 | if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { | 778 | if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { |
| @@ -857,8 +871,19 @@ void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) co | |||
| 857 | rb.PushEnum(bsd_errno); | 871 | rb.PushEnum(bsd_errno); |
| 858 | } | 872 | } |
| 859 | 873 | ||
| 874 | void BSD::OnProxyPacketReceived(const Network::ProxyPacket& packet) { | ||
| 875 | for (auto& optional_descriptor : file_descriptors) { | ||
| 876 | if (!optional_descriptor.has_value()) { | ||
| 877 | continue; | ||
| 878 | } | ||
| 879 | FileDescriptor& descriptor = *optional_descriptor; | ||
| 880 | descriptor.socket.get()->HandleProxyPacket(packet); | ||
| 881 | } | ||
| 882 | } | ||
| 883 | |||
| 860 | BSD::BSD(Core::System& system_, const char* name) | 884 | BSD::BSD(Core::System& system_, const char* name) |
| 861 | : ServiceFramework{system_, name, ServiceThreadType::CreateNew} { | 885 | : ServiceFramework{system_, name, ServiceThreadType::CreateNew}, room_network{ |
| 886 | system_.GetRoomNetwork()} { | ||
| 862 | // clang-format off | 887 | // clang-format off |
| 863 | static const FunctionInfo functions[] = { | 888 | static const FunctionInfo functions[] = { |
| 864 | {0, &BSD::RegisterClient, "RegisterClient"}, | 889 | {0, &BSD::RegisterClient, "RegisterClient"}, |
| @@ -899,6 +924,13 @@ BSD::BSD(Core::System& system_, const char* name) | |||
| 899 | // clang-format on | 924 | // clang-format on |
| 900 | 925 | ||
| 901 | RegisterHandlers(functions); | 926 | RegisterHandlers(functions); |
| 927 | |||
| 928 | if (auto room_member = room_network.GetRoomMember().lock()) { | ||
| 929 | proxy_packet_received = room_member->BindOnProxyPacketReceived( | ||
| 930 | [this](const Network::ProxyPacket& packet) { OnProxyPacketReceived(packet); }); | ||
| 931 | } else { | ||
| 932 | LOG_ERROR(Service, "Network isn't initalized"); | ||
| 933 | } | ||
| 902 | } | 934 | } |
| 903 | 935 | ||
| 904 | BSD::~BSD() = default; | 936 | BSD::~BSD() = default; |
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index 9ea36428d..81e855e0f 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h | |||
| @@ -7,14 +7,17 @@ | |||
| 7 | #include <vector> | 7 | #include <vector> |
| 8 | 8 | ||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/socket_types.h" | ||
| 10 | #include "core/hle/service/service.h" | 11 | #include "core/hle/service/service.h" |
| 11 | #include "core/hle/service/sockets/sockets.h" | 12 | #include "core/hle/service/sockets/sockets.h" |
| 13 | #include "network/network.h" | ||
| 12 | 14 | ||
| 13 | namespace Core { | 15 | namespace Core { |
| 14 | class System; | 16 | class System; |
| 15 | } | 17 | } |
| 16 | 18 | ||
| 17 | namespace Network { | 19 | namespace Network { |
| 20 | class SocketBase; | ||
| 18 | class Socket; | 21 | class Socket; |
| 19 | } // namespace Network | 22 | } // namespace Network |
| 20 | 23 | ||
| @@ -30,7 +33,7 @@ private: | |||
| 30 | static constexpr size_t MAX_FD = 128; | 33 | static constexpr size_t MAX_FD = 128; |
| 31 | 34 | ||
| 32 | struct FileDescriptor { | 35 | struct FileDescriptor { |
| 33 | std::unique_ptr<Network::Socket> socket; | 36 | std::unique_ptr<Network::SocketBase> socket; |
| 34 | s32 flags = 0; | 37 | s32 flags = 0; |
| 35 | bool is_connection_based = false; | 38 | bool is_connection_based = false; |
| 36 | }; | 39 | }; |
| @@ -165,6 +168,14 @@ private: | |||
| 165 | void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept; | 168 | void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept; |
| 166 | 169 | ||
| 167 | std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors; | 170 | std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors; |
| 171 | |||
| 172 | Network::RoomNetwork& room_network; | ||
| 173 | |||
| 174 | /// Callback to parse and handle a received wifi packet. | ||
| 175 | void OnProxyPacketReceived(const Network::ProxyPacket& packet); | ||
| 176 | |||
| 177 | // Callback identifier for the OnProxyPacketReceived event. | ||
| 178 | Network::RoomMember::CallbackHandle<Network::ProxyPacket> proxy_packet_received; | ||
| 168 | }; | 179 | }; |
| 169 | 180 | ||
| 170 | class BSDCFG final : public ServiceFramework<BSDCFG> { | 181 | class BSDCFG final : public ServiceFramework<BSDCFG> { |
diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h index b735b00fc..31b7dad33 100644 --- a/src/core/hle/service/sockets/sockets.h +++ b/src/core/hle/service/sockets/sockets.h | |||
| @@ -22,7 +22,9 @@ enum class Errno : u32 { | |||
| 22 | AGAIN = 11, | 22 | AGAIN = 11, |
| 23 | INVAL = 22, | 23 | INVAL = 22, |
| 24 | MFILE = 24, | 24 | MFILE = 24, |
| 25 | MSGSIZE = 90, | ||
| 25 | NOTCONN = 107, | 26 | NOTCONN = 107, |
| 27 | TIMEDOUT = 110, | ||
| 26 | }; | 28 | }; |
| 27 | 29 | ||
| 28 | enum class Domain : u32 { | 30 | enum class Domain : u32 { |
| @@ -96,10 +98,6 @@ struct Linger { | |||
| 96 | u32 linger; | 98 | u32 linger; |
| 97 | }; | 99 | }; |
| 98 | 100 | ||
| 99 | constexpr u32 FLAG_MSG_DONTWAIT = 0x80; | ||
| 100 | |||
| 101 | constexpr u32 FLAG_O_NONBLOCK = 0x800; | ||
| 102 | |||
| 103 | /// Registers all Sockets services with the specified service manager. | 101 | /// Registers all Sockets services with the specified service manager. |
| 104 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); | 102 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); |
| 105 | 103 | ||
diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp index 2db10ec81..023aa0486 100644 --- a/src/core/hle/service/sockets/sockets_translate.cpp +++ b/src/core/hle/service/sockets/sockets_translate.cpp | |||
| @@ -25,6 +25,8 @@ Errno Translate(Network::Errno value) { | |||
| 25 | return Errno::MFILE; | 25 | return Errno::MFILE; |
| 26 | case Network::Errno::NOTCONN: | 26 | case Network::Errno::NOTCONN: |
| 27 | return Errno::NOTCONN; | 27 | return Errno::NOTCONN; |
| 28 | case Network::Errno::TIMEDOUT: | ||
| 29 | return Errno::TIMEDOUT; | ||
| 28 | default: | 30 | default: |
| 29 | UNIMPLEMENTED_MSG("Unimplemented errno={}", value); | 31 | UNIMPLEMENTED_MSG("Unimplemented errno={}", value); |
| 30 | return Errno::SUCCESS; | 32 | return Errno::SUCCESS; |
diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp index 36c43cc8f..cdf38a2a4 100644 --- a/src/core/internal_network/network.cpp +++ b/src/core/internal_network/network.cpp | |||
| @@ -32,6 +32,7 @@ | |||
| 32 | #include "core/internal_network/network.h" | 32 | #include "core/internal_network/network.h" |
| 33 | #include "core/internal_network/network_interface.h" | 33 | #include "core/internal_network/network_interface.h" |
| 34 | #include "core/internal_network/sockets.h" | 34 | #include "core/internal_network/sockets.h" |
| 35 | #include "network/network.h" | ||
| 35 | 36 | ||
| 36 | namespace Network { | 37 | namespace Network { |
| 37 | 38 | ||
| @@ -114,7 +115,10 @@ Errno TranslateNativeError(int e) { | |||
| 114 | return Errno::NETDOWN; | 115 | return Errno::NETDOWN; |
| 115 | case WSAENETUNREACH: | 116 | case WSAENETUNREACH: |
| 116 | return Errno::NETUNREACH; | 117 | return Errno::NETUNREACH; |
| 118 | case WSAEMSGSIZE: | ||
| 119 | return Errno::MSGSIZE; | ||
| 117 | default: | 120 | default: |
| 121 | UNIMPLEMENTED_MSG("Unimplemented errno={}", e); | ||
| 118 | return Errno::OTHER; | 122 | return Errno::OTHER; |
| 119 | } | 123 | } |
| 120 | } | 124 | } |
| @@ -125,7 +129,6 @@ using SOCKET = int; | |||
| 125 | using WSAPOLLFD = pollfd; | 129 | using WSAPOLLFD = pollfd; |
| 126 | using ULONG = u64; | 130 | using ULONG = u64; |
| 127 | 131 | ||
| 128 | constexpr SOCKET INVALID_SOCKET = -1; | ||
| 129 | constexpr SOCKET SOCKET_ERROR = -1; | 132 | constexpr SOCKET SOCKET_ERROR = -1; |
| 130 | 133 | ||
| 131 | constexpr int SD_RECEIVE = SHUT_RD; | 134 | constexpr int SD_RECEIVE = SHUT_RD; |
| @@ -206,7 +209,10 @@ Errno TranslateNativeError(int e) { | |||
| 206 | return Errno::NETDOWN; | 209 | return Errno::NETDOWN; |
| 207 | case ENETUNREACH: | 210 | case ENETUNREACH: |
| 208 | return Errno::NETUNREACH; | 211 | return Errno::NETUNREACH; |
| 212 | case EMSGSIZE: | ||
| 213 | return Errno::MSGSIZE; | ||
| 209 | default: | 214 | default: |
| 215 | UNIMPLEMENTED_MSG("Unimplemented errno={}", e); | ||
| 210 | return Errno::OTHER; | 216 | return Errno::OTHER; |
| 211 | } | 217 | } |
| 212 | } | 218 | } |
| @@ -329,16 +335,6 @@ PollEvents TranslatePollRevents(short revents) { | |||
| 329 | return result; | 335 | return result; |
| 330 | } | 336 | } |
| 331 | 337 | ||
| 332 | template <typename T> | ||
| 333 | Errno SetSockOpt(SOCKET fd, int option, T value) { | ||
| 334 | const int result = | ||
| 335 | setsockopt(fd, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value)); | ||
| 336 | if (result != SOCKET_ERROR) { | ||
| 337 | return Errno::SUCCESS; | ||
| 338 | } | ||
| 339 | return GetAndLogLastError(); | ||
| 340 | } | ||
| 341 | |||
| 342 | } // Anonymous namespace | 338 | } // Anonymous namespace |
| 343 | 339 | ||
| 344 | NetworkInstance::NetworkInstance() { | 340 | NetworkInstance::NetworkInstance() { |
| @@ -350,26 +346,16 @@ NetworkInstance::~NetworkInstance() { | |||
| 350 | } | 346 | } |
| 351 | 347 | ||
| 352 | std::optional<IPv4Address> GetHostIPv4Address() { | 348 | std::optional<IPv4Address> GetHostIPv4Address() { |
| 353 | const std::string& selected_network_interface = Settings::values.network_interface.GetValue(); | 349 | const auto network_interface = Network::GetSelectedNetworkInterface(); |
| 354 | const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); | 350 | if (!network_interface.has_value()) { |
| 355 | if (network_interfaces.size() == 0) { | 351 | LOG_ERROR(Network, "GetSelectedNetworkInterface returned no interface"); |
| 356 | LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); | ||
| 357 | return {}; | 352 | return {}; |
| 358 | } | 353 | } |
| 359 | 354 | ||
| 360 | const auto res = | 355 | std::array<char, 16> ip_addr = {}; |
| 361 | std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) { | 356 | ASSERT(inet_ntop(AF_INET, &network_interface->ip_address, ip_addr.data(), sizeof(ip_addr)) != |
| 362 | return iface.name == selected_network_interface; | 357 | nullptr); |
| 363 | }); | 358 | return TranslateIPv4(network_interface->ip_address); |
| 364 | |||
| 365 | if (res != network_interfaces.end()) { | ||
| 366 | char ip_addr[16] = {}; | ||
| 367 | ASSERT(inet_ntop(AF_INET, &res->ip_address, ip_addr, sizeof(ip_addr)) != nullptr); | ||
| 368 | return TranslateIPv4(res->ip_address); | ||
| 369 | } else { | ||
| 370 | LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); | ||
| 371 | return {}; | ||
| 372 | } | ||
| 373 | } | 359 | } |
| 374 | 360 | ||
| 375 | std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { | 361 | std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { |
| @@ -412,7 +398,19 @@ Socket::~Socket() { | |||
| 412 | fd = INVALID_SOCKET; | 398 | fd = INVALID_SOCKET; |
| 413 | } | 399 | } |
| 414 | 400 | ||
| 415 | Socket::Socket(Socket&& rhs) noexcept : fd{std::exchange(rhs.fd, INVALID_SOCKET)} {} | 401 | Socket::Socket(Socket&& rhs) noexcept { |
| 402 | fd = std::exchange(rhs.fd, INVALID_SOCKET); | ||
| 403 | } | ||
| 404 | |||
| 405 | template <typename T> | ||
| 406 | Errno Socket::SetSockOpt(SOCKET fd_, int option, T value) { | ||
| 407 | const int result = | ||
| 408 | setsockopt(fd_, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value)); | ||
| 409 | if (result != SOCKET_ERROR) { | ||
| 410 | return Errno::SUCCESS; | ||
| 411 | } | ||
| 412 | return GetAndLogLastError(); | ||
| 413 | } | ||
| 416 | 414 | ||
| 417 | Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { | 415 | Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { |
| 418 | fd = socket(TranslateDomain(domain), TranslateType(type), TranslateProtocol(protocol)); | 416 | fd = socket(TranslateDomain(domain), TranslateType(type), TranslateProtocol(protocol)); |
| @@ -423,7 +421,7 @@ Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { | |||
| 423 | return GetAndLogLastError(); | 421 | return GetAndLogLastError(); |
| 424 | } | 422 | } |
| 425 | 423 | ||
| 426 | std::pair<Socket::AcceptResult, Errno> Socket::Accept() { | 424 | std::pair<SocketBase::AcceptResult, Errno> Socket::Accept() { |
| 427 | sockaddr addr; | 425 | sockaddr addr; |
| 428 | socklen_t addrlen = sizeof(addr); | 426 | socklen_t addrlen = sizeof(addr); |
| 429 | const SOCKET new_socket = accept(fd, &addr, &addrlen); | 427 | const SOCKET new_socket = accept(fd, &addr, &addrlen); |
| @@ -634,4 +632,8 @@ bool Socket::IsOpened() const { | |||
| 634 | return fd != INVALID_SOCKET; | 632 | return fd != INVALID_SOCKET; |
| 635 | } | 633 | } |
| 636 | 634 | ||
| 635 | void Socket::HandleProxyPacket(const ProxyPacket& packet) { | ||
| 636 | LOG_WARNING(Network, "ProxyPacket received, but not in Proxy mode!"); | ||
| 637 | } | ||
| 638 | |||
| 637 | } // namespace Network | 639 | } // namespace Network |
diff --git a/src/core/internal_network/network.h b/src/core/internal_network/network.h index 10e5ef10d..36994c22e 100644 --- a/src/core/internal_network/network.h +++ b/src/core/internal_network/network.h | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | 8 | ||
| 9 | #include "common/common_funcs.h" | 9 | #include "common/common_funcs.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/socket_types.h" | ||
| 11 | 12 | ||
| 12 | #ifdef _WIN32 | 13 | #ifdef _WIN32 |
| 13 | #include <winsock2.h> | 14 | #include <winsock2.h> |
| @@ -17,6 +18,7 @@ | |||
| 17 | 18 | ||
| 18 | namespace Network { | 19 | namespace Network { |
| 19 | 20 | ||
| 21 | class SocketBase; | ||
| 20 | class Socket; | 22 | class Socket; |
| 21 | 23 | ||
| 22 | /// Error code for network functions | 24 | /// Error code for network functions |
| @@ -31,46 +33,11 @@ enum class Errno { | |||
| 31 | HOSTUNREACH, | 33 | HOSTUNREACH, |
| 32 | NETDOWN, | 34 | NETDOWN, |
| 33 | NETUNREACH, | 35 | NETUNREACH, |
| 36 | TIMEDOUT, | ||
| 37 | MSGSIZE, | ||
| 34 | OTHER, | 38 | OTHER, |
| 35 | }; | 39 | }; |
| 36 | 40 | ||
| 37 | /// Address families | ||
| 38 | enum class Domain { | ||
| 39 | INET, ///< Address family for IPv4 | ||
| 40 | }; | ||
| 41 | |||
| 42 | /// Socket types | ||
| 43 | enum class Type { | ||
| 44 | STREAM, | ||
| 45 | DGRAM, | ||
| 46 | RAW, | ||
| 47 | SEQPACKET, | ||
| 48 | }; | ||
| 49 | |||
| 50 | /// Protocol values for sockets | ||
| 51 | enum class Protocol { | ||
| 52 | ICMP, | ||
| 53 | TCP, | ||
| 54 | UDP, | ||
| 55 | }; | ||
| 56 | |||
| 57 | /// Shutdown mode | ||
| 58 | enum class ShutdownHow { | ||
| 59 | RD, | ||
| 60 | WR, | ||
| 61 | RDWR, | ||
| 62 | }; | ||
| 63 | |||
| 64 | /// Array of IPv4 address | ||
| 65 | using IPv4Address = std::array<u8, 4>; | ||
| 66 | |||
| 67 | /// Cross-platform sockaddr structure | ||
| 68 | struct SockAddrIn { | ||
| 69 | Domain family; | ||
| 70 | IPv4Address ip; | ||
| 71 | u16 portno; | ||
| 72 | }; | ||
| 73 | |||
| 74 | /// Cross-platform poll fd structure | 41 | /// Cross-platform poll fd structure |
| 75 | 42 | ||
| 76 | enum class PollEvents : u16 { | 43 | enum class PollEvents : u16 { |
| @@ -86,7 +53,7 @@ enum class PollEvents : u16 { | |||
| 86 | DECLARE_ENUM_FLAG_OPERATORS(PollEvents); | 53 | DECLARE_ENUM_FLAG_OPERATORS(PollEvents); |
| 87 | 54 | ||
| 88 | struct PollFD { | 55 | struct PollFD { |
| 89 | Socket* socket; | 56 | SocketBase* socket; |
| 90 | PollEvents events; | 57 | PollEvents events; |
| 91 | PollEvents revents; | 58 | PollEvents revents; |
| 92 | }; | 59 | }; |
diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp new file mode 100644 index 000000000..49d067f4c --- /dev/null +++ b/src/core/internal_network/socket_proxy.cpp | |||
| @@ -0,0 +1,284 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <chrono> | ||
| 5 | #include <thread> | ||
| 6 | |||
| 7 | #include "common/assert.h" | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "core/internal_network/network.h" | ||
| 10 | #include "core/internal_network/network_interface.h" | ||
| 11 | #include "core/internal_network/socket_proxy.h" | ||
| 12 | |||
| 13 | namespace Network { | ||
| 14 | |||
| 15 | ProxySocket::ProxySocket(RoomNetwork& room_network_) noexcept : room_network{room_network_} {} | ||
| 16 | |||
| 17 | ProxySocket::~ProxySocket() { | ||
| 18 | if (fd == INVALID_SOCKET) { | ||
| 19 | return; | ||
| 20 | } | ||
| 21 | fd = INVALID_SOCKET; | ||
| 22 | } | ||
| 23 | |||
| 24 | void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) { | ||
| 25 | if (protocol != packet.protocol || local_endpoint.portno != packet.remote_endpoint.portno || | ||
| 26 | closed) { | ||
| 27 | return; | ||
| 28 | } | ||
| 29 | std::lock_guard guard(packets_mutex); | ||
| 30 | received_packets.push(packet); | ||
| 31 | } | ||
| 32 | |||
| 33 | template <typename T> | ||
| 34 | Errno ProxySocket::SetSockOpt(SOCKET fd_, int option, T value) { | ||
| 35 | LOG_DEBUG(Network, "(STUBBED) called"); | ||
| 36 | return Errno::SUCCESS; | ||
| 37 | } | ||
| 38 | |||
| 39 | Errno ProxySocket::Initialize(Domain domain, Type type, Protocol socket_protocol) { | ||
| 40 | protocol = socket_protocol; | ||
| 41 | SetSockOpt(fd, SO_TYPE, type); | ||
| 42 | |||
| 43 | return Errno::SUCCESS; | ||
| 44 | } | ||
| 45 | |||
| 46 | std::pair<ProxySocket::AcceptResult, Errno> ProxySocket::Accept() { | ||
| 47 | LOG_WARNING(Network, "(STUBBED) called"); | ||
| 48 | return {AcceptResult{}, Errno::SUCCESS}; | ||
| 49 | } | ||
| 50 | |||
| 51 | Errno ProxySocket::Connect(SockAddrIn addr_in) { | ||
| 52 | LOG_WARNING(Network, "(STUBBED) called"); | ||
| 53 | return Errno::SUCCESS; | ||
| 54 | } | ||
| 55 | |||
| 56 | std::pair<SockAddrIn, Errno> ProxySocket::GetPeerName() { | ||
| 57 | LOG_WARNING(Network, "(STUBBED) called"); | ||
| 58 | return {SockAddrIn{}, Errno::SUCCESS}; | ||
| 59 | } | ||
| 60 | |||
| 61 | std::pair<SockAddrIn, Errno> ProxySocket::GetSockName() { | ||
| 62 | LOG_WARNING(Network, "(STUBBED) called"); | ||
| 63 | return {SockAddrIn{}, Errno::SUCCESS}; | ||
| 64 | } | ||
| 65 | |||
| 66 | Errno ProxySocket::Bind(SockAddrIn addr) { | ||
| 67 | if (is_bound) { | ||
| 68 | LOG_WARNING(Network, "Rebinding Socket is unimplemented!"); | ||
| 69 | return Errno::SUCCESS; | ||
| 70 | } | ||
| 71 | local_endpoint = addr; | ||
| 72 | is_bound = true; | ||
| 73 | |||
| 74 | return Errno::SUCCESS; | ||
| 75 | } | ||
| 76 | |||
| 77 | Errno ProxySocket::Listen(s32 backlog) { | ||
| 78 | LOG_WARNING(Network, "(STUBBED) called"); | ||
| 79 | return Errno::SUCCESS; | ||
| 80 | } | ||
| 81 | |||
| 82 | Errno ProxySocket::Shutdown(ShutdownHow how) { | ||
| 83 | LOG_WARNING(Network, "(STUBBED) called"); | ||
| 84 | return Errno::SUCCESS; | ||
| 85 | } | ||
| 86 | |||
| 87 | std::pair<s32, Errno> ProxySocket::Recv(int flags, std::vector<u8>& message) { | ||
| 88 | LOG_WARNING(Network, "(STUBBED) called"); | ||
| 89 | ASSERT(flags == 0); | ||
| 90 | ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); | ||
| 91 | |||
| 92 | return {static_cast<s32>(0), Errno::SUCCESS}; | ||
| 93 | } | ||
| 94 | |||
| 95 | std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) { | ||
| 96 | ASSERT(flags == 0); | ||
| 97 | ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); | ||
| 98 | |||
| 99 | // TODO (flTobi): Verify the timeout behavior and break when connection is lost | ||
| 100 | const auto timestamp = std::chrono::steady_clock::now(); | ||
| 101 | // When receive_timeout is set to zero, the socket is supposed to wait indefinitely until a | ||
| 102 | // packet arrives. In order to prevent lost packets from hanging the emulation thread, we set | ||
| 103 | // the timeout to 5s instead | ||
| 104 | const auto timeout = receive_timeout == 0 ? 5000 : receive_timeout; | ||
| 105 | while (true) { | ||
| 106 | { | ||
| 107 | std::lock_guard guard(packets_mutex); | ||
| 108 | if (received_packets.size() > 0) { | ||
| 109 | return ReceivePacket(flags, message, addr, message.size()); | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | if (!blocking) { | ||
| 114 | return {-1, Errno::AGAIN}; | ||
| 115 | } | ||
| 116 | |||
| 117 | std::this_thread::yield(); | ||
| 118 | |||
| 119 | const auto time_diff = std::chrono::steady_clock::now() - timestamp; | ||
| 120 | const auto time_diff_ms = | ||
| 121 | std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count(); | ||
| 122 | |||
| 123 | if (time_diff_ms > timeout) { | ||
| 124 | return {-1, Errno::TIMEDOUT}; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::vector<u8>& message, | ||
| 130 | SockAddrIn* addr, std::size_t max_length) { | ||
| 131 | ProxyPacket& packet = received_packets.front(); | ||
| 132 | if (addr) { | ||
| 133 | addr->family = Domain::INET; | ||
| 134 | addr->ip = packet.local_endpoint.ip; // The senders ip address | ||
| 135 | addr->portno = packet.local_endpoint.portno; // The senders port number | ||
| 136 | } | ||
| 137 | |||
| 138 | bool peek = (flags & FLAG_MSG_PEEK) != 0; | ||
| 139 | std::size_t read_bytes; | ||
| 140 | if (packet.data.size() > max_length) { | ||
| 141 | read_bytes = max_length; | ||
| 142 | message.clear(); | ||
| 143 | std::copy(packet.data.begin(), packet.data.begin() + read_bytes, | ||
| 144 | std::back_inserter(message)); | ||
| 145 | message.resize(max_length); | ||
| 146 | |||
| 147 | if (protocol == Protocol::UDP) { | ||
| 148 | if (!peek) { | ||
| 149 | received_packets.pop(); | ||
| 150 | } | ||
| 151 | return {-1, Errno::MSGSIZE}; | ||
| 152 | } else if (protocol == Protocol::TCP) { | ||
| 153 | std::vector<u8> numArray(packet.data.size() - max_length); | ||
| 154 | std::copy(packet.data.begin() + max_length, packet.data.end(), | ||
| 155 | std::back_inserter(numArray)); | ||
| 156 | packet.data = numArray; | ||
| 157 | } | ||
| 158 | } else { | ||
| 159 | read_bytes = packet.data.size(); | ||
| 160 | message.clear(); | ||
| 161 | std::copy(packet.data.begin(), packet.data.end(), std::back_inserter(message)); | ||
| 162 | message.resize(max_length); | ||
| 163 | if (!peek) { | ||
| 164 | received_packets.pop(); | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 168 | return {static_cast<u32>(read_bytes), Errno::SUCCESS}; | ||
| 169 | } | ||
| 170 | |||
| 171 | std::pair<s32, Errno> ProxySocket::Send(const std::vector<u8>& message, int flags) { | ||
| 172 | LOG_WARNING(Network, "(STUBBED) called"); | ||
| 173 | ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); | ||
| 174 | ASSERT(flags == 0); | ||
| 175 | |||
| 176 | return {static_cast<s32>(0), Errno::SUCCESS}; | ||
| 177 | } | ||
| 178 | |||
| 179 | void ProxySocket::SendPacket(ProxyPacket& packet) { | ||
| 180 | if (auto room_member = room_network.GetRoomMember().lock()) { | ||
| 181 | if (room_member->IsConnected()) { | ||
| 182 | room_member->SendProxyPacket(packet); | ||
| 183 | } | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, const std::vector<u8>& message, | ||
| 188 | const SockAddrIn* addr) { | ||
| 189 | ASSERT(flags == 0); | ||
| 190 | |||
| 191 | if (!is_bound) { | ||
| 192 | LOG_ERROR(Network, "ProxySocket is not bound!"); | ||
| 193 | return {static_cast<s32>(message.size()), Errno::SUCCESS}; | ||
| 194 | } | ||
| 195 | |||
| 196 | if (auto room_member = room_network.GetRoomMember().lock()) { | ||
| 197 | if (!room_member->IsConnected()) { | ||
| 198 | return {static_cast<s32>(message.size()), Errno::SUCCESS}; | ||
| 199 | } | ||
| 200 | } | ||
| 201 | |||
| 202 | ProxyPacket packet; | ||
| 203 | packet.local_endpoint = local_endpoint; | ||
| 204 | packet.remote_endpoint = *addr; | ||
| 205 | packet.protocol = protocol; | ||
| 206 | packet.broadcast = broadcast; | ||
| 207 | |||
| 208 | auto& ip = local_endpoint.ip; | ||
| 209 | auto ipv4 = Network::GetHostIPv4Address(); | ||
| 210 | // If the ip is all zeroes (INADDR_ANY) or if it matches the hosts ip address, | ||
| 211 | // replace it with a "fake" routing address | ||
| 212 | if (std::all_of(ip.begin(), ip.end(), [](u8 i) { return i == 0; }) || (ipv4 && ipv4 == ip)) { | ||
| 213 | if (auto room_member = room_network.GetRoomMember().lock()) { | ||
| 214 | packet.local_endpoint.ip = room_member->GetFakeIpAddress(); | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | packet.data.clear(); | ||
| 219 | std::copy(message.begin(), message.end(), std::back_inserter(packet.data)); | ||
| 220 | |||
| 221 | SendPacket(packet); | ||
| 222 | |||
| 223 | return {static_cast<s32>(message.size()), Errno::SUCCESS}; | ||
| 224 | } | ||
| 225 | |||
| 226 | Errno ProxySocket::Close() { | ||
| 227 | fd = INVALID_SOCKET; | ||
| 228 | closed = true; | ||
| 229 | |||
| 230 | return Errno::SUCCESS; | ||
| 231 | } | ||
| 232 | |||
| 233 | Errno ProxySocket::SetLinger(bool enable, u32 linger) { | ||
| 234 | struct Linger { | ||
| 235 | u16 linger_enable; | ||
| 236 | u16 linger_time; | ||
| 237 | } values; | ||
| 238 | values.linger_enable = enable ? 1 : 0; | ||
| 239 | values.linger_time = static_cast<u16>(linger); | ||
| 240 | |||
| 241 | return SetSockOpt(fd, SO_LINGER, values); | ||
| 242 | } | ||
| 243 | |||
| 244 | Errno ProxySocket::SetReuseAddr(bool enable) { | ||
| 245 | return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0); | ||
| 246 | } | ||
| 247 | |||
| 248 | Errno ProxySocket::SetBroadcast(bool enable) { | ||
| 249 | broadcast = enable; | ||
| 250 | return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0); | ||
| 251 | } | ||
| 252 | |||
| 253 | Errno ProxySocket::SetSndBuf(u32 value) { | ||
| 254 | return SetSockOpt(fd, SO_SNDBUF, value); | ||
| 255 | } | ||
| 256 | |||
| 257 | Errno ProxySocket::SetKeepAlive(bool enable) { | ||
| 258 | return Errno::SUCCESS; | ||
| 259 | } | ||
| 260 | |||
| 261 | Errno ProxySocket::SetRcvBuf(u32 value) { | ||
| 262 | return SetSockOpt(fd, SO_RCVBUF, value); | ||
| 263 | } | ||
| 264 | |||
| 265 | Errno ProxySocket::SetSndTimeo(u32 value) { | ||
| 266 | send_timeout = value; | ||
| 267 | return SetSockOpt(fd, SO_SNDTIMEO, static_cast<int>(value)); | ||
| 268 | } | ||
| 269 | |||
| 270 | Errno ProxySocket::SetRcvTimeo(u32 value) { | ||
| 271 | receive_timeout = value; | ||
| 272 | return SetSockOpt(fd, SO_RCVTIMEO, static_cast<int>(value)); | ||
| 273 | } | ||
| 274 | |||
| 275 | Errno ProxySocket::SetNonBlock(bool enable) { | ||
| 276 | blocking = !enable; | ||
| 277 | return Errno::SUCCESS; | ||
| 278 | } | ||
| 279 | |||
| 280 | bool ProxySocket::IsOpened() const { | ||
| 281 | return fd != INVALID_SOCKET; | ||
| 282 | } | ||
| 283 | |||
| 284 | } // namespace Network | ||
diff --git a/src/core/internal_network/socket_proxy.h b/src/core/internal_network/socket_proxy.h new file mode 100644 index 000000000..f12b5f567 --- /dev/null +++ b/src/core/internal_network/socket_proxy.h | |||
| @@ -0,0 +1,97 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <mutex> | ||
| 7 | #include <vector> | ||
| 8 | #include <queue> | ||
| 9 | |||
| 10 | #include "common/common_funcs.h" | ||
| 11 | #include "core/internal_network/sockets.h" | ||
| 12 | #include "network/network.h" | ||
| 13 | |||
| 14 | namespace Network { | ||
| 15 | |||
| 16 | class ProxySocket : public SocketBase { | ||
| 17 | public: | ||
| 18 | YUZU_NON_COPYABLE(ProxySocket); | ||
| 19 | YUZU_NON_MOVEABLE(ProxySocket); | ||
| 20 | |||
| 21 | explicit ProxySocket(RoomNetwork& room_network_) noexcept; | ||
| 22 | ~ProxySocket() override; | ||
| 23 | |||
| 24 | void HandleProxyPacket(const ProxyPacket& packet) override; | ||
| 25 | |||
| 26 | Errno Initialize(Domain domain, Type type, Protocol socket_protocol) override; | ||
| 27 | |||
| 28 | Errno Close() override; | ||
| 29 | |||
| 30 | std::pair<AcceptResult, Errno> Accept() override; | ||
| 31 | |||
| 32 | Errno Connect(SockAddrIn addr_in) override; | ||
| 33 | |||
| 34 | std::pair<SockAddrIn, Errno> GetPeerName() override; | ||
| 35 | |||
| 36 | std::pair<SockAddrIn, Errno> GetSockName() override; | ||
| 37 | |||
| 38 | Errno Bind(SockAddrIn addr) override; | ||
| 39 | |||
| 40 | Errno Listen(s32 backlog) override; | ||
| 41 | |||
| 42 | Errno Shutdown(ShutdownHow how) override; | ||
| 43 | |||
| 44 | std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) override; | ||
| 45 | |||
| 46 | std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override; | ||
| 47 | |||
| 48 | std::pair<s32, Errno> ReceivePacket(int flags, std::vector<u8>& message, SockAddrIn* addr, | ||
| 49 | std::size_t max_length); | ||
| 50 | |||
| 51 | std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) override; | ||
| 52 | |||
| 53 | void SendPacket(ProxyPacket& packet); | ||
| 54 | |||
| 55 | std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, | ||
| 56 | const SockAddrIn* addr) override; | ||
| 57 | |||
| 58 | Errno SetLinger(bool enable, u32 linger) override; | ||
| 59 | |||
| 60 | Errno SetReuseAddr(bool enable) override; | ||
| 61 | |||
| 62 | Errno SetBroadcast(bool enable) override; | ||
| 63 | |||
| 64 | Errno SetKeepAlive(bool enable) override; | ||
| 65 | |||
| 66 | Errno SetSndBuf(u32 value) override; | ||
| 67 | |||
| 68 | Errno SetRcvBuf(u32 value) override; | ||
| 69 | |||
| 70 | Errno SetSndTimeo(u32 value) override; | ||
| 71 | |||
| 72 | Errno SetRcvTimeo(u32 value) override; | ||
| 73 | |||
| 74 | Errno SetNonBlock(bool enable) override; | ||
| 75 | |||
| 76 | template <typename T> | ||
| 77 | Errno SetSockOpt(SOCKET fd, int option, T value); | ||
| 78 | |||
| 79 | bool IsOpened() const override; | ||
| 80 | |||
| 81 | private: | ||
| 82 | bool broadcast = false; | ||
| 83 | bool closed = false; | ||
| 84 | u32 send_timeout = 0; | ||
| 85 | u32 receive_timeout = 0; | ||
| 86 | bool is_bound = false; | ||
| 87 | SockAddrIn local_endpoint{}; | ||
| 88 | bool blocking = true; | ||
| 89 | std::queue<ProxyPacket> received_packets; | ||
| 90 | Protocol protocol; | ||
| 91 | |||
| 92 | std::mutex packets_mutex; | ||
| 93 | |||
| 94 | RoomNetwork& room_network; | ||
| 95 | }; | ||
| 96 | |||
| 97 | } // namespace Network | ||
diff --git a/src/core/internal_network/sockets.h b/src/core/internal_network/sockets.h index 77e27e928..a70429b19 100644 --- a/src/core/internal_network/sockets.h +++ b/src/core/internal_network/sockets.h | |||
| @@ -14,20 +14,88 @@ | |||
| 14 | 14 | ||
| 15 | #include "common/common_types.h" | 15 | #include "common/common_types.h" |
| 16 | #include "core/internal_network/network.h" | 16 | #include "core/internal_network/network.h" |
| 17 | #include "network/network.h" | ||
| 17 | 18 | ||
| 18 | // TODO: C++20 Replace std::vector usages with std::span | 19 | // TODO: C++20 Replace std::vector usages with std::span |
| 19 | 20 | ||
| 20 | namespace Network { | 21 | namespace Network { |
| 21 | 22 | ||
| 22 | class Socket { | 23 | class SocketBase { |
| 23 | public: | 24 | public: |
| 25 | #ifdef YUZU_UNIX | ||
| 26 | using SOCKET = int; | ||
| 27 | static constexpr SOCKET INVALID_SOCKET = -1; | ||
| 28 | static constexpr SOCKET SOCKET_ERROR = -1; | ||
| 29 | #endif | ||
| 30 | |||
| 24 | struct AcceptResult { | 31 | struct AcceptResult { |
| 25 | std::unique_ptr<Socket> socket; | 32 | std::unique_ptr<SocketBase> socket; |
| 26 | SockAddrIn sockaddr_in; | 33 | SockAddrIn sockaddr_in; |
| 27 | }; | 34 | }; |
| 35 | virtual ~SocketBase() = default; | ||
| 36 | |||
| 37 | virtual SocketBase& operator=(const SocketBase&) = delete; | ||
| 38 | |||
| 39 | // Avoid closing sockets implicitly | ||
| 40 | virtual SocketBase& operator=(SocketBase&&) noexcept = delete; | ||
| 41 | |||
| 42 | virtual Errno Initialize(Domain domain, Type type, Protocol protocol) = 0; | ||
| 43 | |||
| 44 | virtual Errno Close() = 0; | ||
| 45 | |||
| 46 | virtual std::pair<AcceptResult, Errno> Accept() = 0; | ||
| 47 | |||
| 48 | virtual Errno Connect(SockAddrIn addr_in) = 0; | ||
| 49 | |||
| 50 | virtual std::pair<SockAddrIn, Errno> GetPeerName() = 0; | ||
| 51 | |||
| 52 | virtual std::pair<SockAddrIn, Errno> GetSockName() = 0; | ||
| 53 | |||
| 54 | virtual Errno Bind(SockAddrIn addr) = 0; | ||
| 55 | |||
| 56 | virtual Errno Listen(s32 backlog) = 0; | ||
| 57 | |||
| 58 | virtual Errno Shutdown(ShutdownHow how) = 0; | ||
| 59 | |||
| 60 | virtual std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) = 0; | ||
| 61 | |||
| 62 | virtual std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, | ||
| 63 | SockAddrIn* addr) = 0; | ||
| 64 | |||
| 65 | virtual std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) = 0; | ||
| 66 | |||
| 67 | virtual std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, | ||
| 68 | const SockAddrIn* addr) = 0; | ||
| 69 | |||
| 70 | virtual Errno SetLinger(bool enable, u32 linger) = 0; | ||
| 28 | 71 | ||
| 29 | explicit Socket() = default; | 72 | virtual Errno SetReuseAddr(bool enable) = 0; |
| 30 | ~Socket(); | 73 | |
| 74 | virtual Errno SetKeepAlive(bool enable) = 0; | ||
| 75 | |||
| 76 | virtual Errno SetBroadcast(bool enable) = 0; | ||
| 77 | |||
| 78 | virtual Errno SetSndBuf(u32 value) = 0; | ||
| 79 | |||
| 80 | virtual Errno SetRcvBuf(u32 value) = 0; | ||
| 81 | |||
| 82 | virtual Errno SetSndTimeo(u32 value) = 0; | ||
| 83 | |||
| 84 | virtual Errno SetRcvTimeo(u32 value) = 0; | ||
| 85 | |||
| 86 | virtual Errno SetNonBlock(bool enable) = 0; | ||
| 87 | |||
| 88 | virtual bool IsOpened() const = 0; | ||
| 89 | |||
| 90 | virtual void HandleProxyPacket(const ProxyPacket& packet) = 0; | ||
| 91 | |||
| 92 | SOCKET fd = INVALID_SOCKET; | ||
| 93 | }; | ||
| 94 | |||
| 95 | class Socket : public SocketBase { | ||
| 96 | public: | ||
| 97 | Socket() = default; | ||
| 98 | ~Socket() override; | ||
| 31 | 99 | ||
| 32 | Socket(const Socket&) = delete; | 100 | Socket(const Socket&) = delete; |
| 33 | Socket& operator=(const Socket&) = delete; | 101 | Socket& operator=(const Socket&) = delete; |
| @@ -37,57 +105,57 @@ public: | |||
| 37 | // Avoid closing sockets implicitly | 105 | // Avoid closing sockets implicitly |
| 38 | Socket& operator=(Socket&&) noexcept = delete; | 106 | Socket& operator=(Socket&&) noexcept = delete; |
| 39 | 107 | ||
| 40 | Errno Initialize(Domain domain, Type type, Protocol protocol); | 108 | Errno Initialize(Domain domain, Type type, Protocol protocol) override; |
| 41 | 109 | ||
| 42 | Errno Close(); | 110 | Errno Close() override; |
| 43 | 111 | ||
| 44 | std::pair<AcceptResult, Errno> Accept(); | 112 | std::pair<AcceptResult, Errno> Accept() override; |
| 45 | 113 | ||
| 46 | Errno Connect(SockAddrIn addr_in); | 114 | Errno Connect(SockAddrIn addr_in) override; |
| 47 | 115 | ||
| 48 | std::pair<SockAddrIn, Errno> GetPeerName(); | 116 | std::pair<SockAddrIn, Errno> GetPeerName() override; |
| 49 | 117 | ||
| 50 | std::pair<SockAddrIn, Errno> GetSockName(); | 118 | std::pair<SockAddrIn, Errno> GetSockName() override; |
| 51 | 119 | ||
| 52 | Errno Bind(SockAddrIn addr); | 120 | Errno Bind(SockAddrIn addr) override; |
| 53 | 121 | ||
| 54 | Errno Listen(s32 backlog); | 122 | Errno Listen(s32 backlog) override; |
| 55 | 123 | ||
| 56 | Errno Shutdown(ShutdownHow how); | 124 | Errno Shutdown(ShutdownHow how) override; |
| 57 | 125 | ||
| 58 | std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message); | 126 | std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) override; |
| 59 | 127 | ||
| 60 | std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr); | 128 | std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override; |
| 61 | 129 | ||
| 62 | std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags); | 130 | std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) override; |
| 63 | 131 | ||
| 64 | std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, const SockAddrIn* addr); | 132 | std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, |
| 133 | const SockAddrIn* addr) override; | ||
| 65 | 134 | ||
| 66 | Errno SetLinger(bool enable, u32 linger); | 135 | Errno SetLinger(bool enable, u32 linger) override; |
| 67 | 136 | ||
| 68 | Errno SetReuseAddr(bool enable); | 137 | Errno SetReuseAddr(bool enable) override; |
| 69 | 138 | ||
| 70 | Errno SetKeepAlive(bool enable); | 139 | Errno SetKeepAlive(bool enable) override; |
| 71 | 140 | ||
| 72 | Errno SetBroadcast(bool enable); | 141 | Errno SetBroadcast(bool enable) override; |
| 73 | 142 | ||
| 74 | Errno SetSndBuf(u32 value); | 143 | Errno SetSndBuf(u32 value) override; |
| 75 | 144 | ||
| 76 | Errno SetRcvBuf(u32 value); | 145 | Errno SetRcvBuf(u32 value) override; |
| 77 | 146 | ||
| 78 | Errno SetSndTimeo(u32 value); | 147 | Errno SetSndTimeo(u32 value) override; |
| 79 | 148 | ||
| 80 | Errno SetRcvTimeo(u32 value); | 149 | Errno SetRcvTimeo(u32 value) override; |
| 81 | 150 | ||
| 82 | Errno SetNonBlock(bool enable); | 151 | Errno SetNonBlock(bool enable) override; |
| 83 | 152 | ||
| 84 | bool IsOpened() const; | 153 | template <typename T> |
| 154 | Errno SetSockOpt(SOCKET fd, int option, T value); | ||
| 85 | 155 | ||
| 86 | #if defined(_WIN32) | 156 | bool IsOpened() const override; |
| 87 | SOCKET fd = INVALID_SOCKET; | 157 | |
| 88 | #elif YUZU_UNIX | 158 | void HandleProxyPacket(const ProxyPacket& packet) override; |
| 89 | int fd = -1; | ||
| 90 | #endif | ||
| 91 | }; | 159 | }; |
| 92 | 160 | ||
| 93 | std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout); | 161 | std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout); |
diff --git a/src/dedicated_room/CMakeLists.txt b/src/dedicated_room/CMakeLists.txt new file mode 100644 index 000000000..b674b915b --- /dev/null +++ b/src/dedicated_room/CMakeLists.txt | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | # SPDX-FileCopyrightText: 2017 Citra Emulator Project | ||
| 2 | # SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules) | ||
| 5 | |||
| 6 | add_executable(yuzu-room | ||
| 7 | yuzu_room.cpp | ||
| 8 | yuzu_room.rc | ||
| 9 | ) | ||
| 10 | |||
| 11 | create_target_directory_groups(yuzu-room) | ||
| 12 | |||
| 13 | target_link_libraries(yuzu-room PRIVATE common core network) | ||
| 14 | if (ENABLE_WEB_SERVICE) | ||
| 15 | target_compile_definitions(yuzu-room PRIVATE -DENABLE_WEB_SERVICE) | ||
| 16 | target_link_libraries(yuzu-room PRIVATE web_service) | ||
| 17 | endif() | ||
| 18 | |||
| 19 | target_link_libraries(yuzu-room PRIVATE mbedtls) | ||
| 20 | if (MSVC) | ||
| 21 | target_link_libraries(yuzu-room PRIVATE getopt) | ||
| 22 | endif() | ||
| 23 | target_link_libraries(yuzu-room PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) | ||
| 24 | |||
| 25 | if(UNIX AND NOT APPLE) | ||
| 26 | install(TARGETS yuzu-room RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") | ||
| 27 | endif() | ||
diff --git a/src/dedicated_room/yuzu_room.cpp b/src/dedicated_room/yuzu_room.cpp new file mode 100644 index 000000000..482e772fb --- /dev/null +++ b/src/dedicated_room/yuzu_room.cpp | |||
| @@ -0,0 +1,375 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <chrono> | ||
| 5 | #include <fstream> | ||
| 6 | #include <iostream> | ||
| 7 | #include <memory> | ||
| 8 | #include <regex> | ||
| 9 | #include <string> | ||
| 10 | #include <thread> | ||
| 11 | |||
| 12 | #ifdef _WIN32 | ||
| 13 | // windows.h needs to be included before shellapi.h | ||
| 14 | #include <windows.h> | ||
| 15 | |||
| 16 | #include <shellapi.h> | ||
| 17 | #endif | ||
| 18 | |||
| 19 | #include <mbedtls/base64.h> | ||
| 20 | #include "common/common_types.h" | ||
| 21 | #include "common/detached_tasks.h" | ||
| 22 | #include "common/fs/file.h" | ||
| 23 | #include "common/fs/fs.h" | ||
| 24 | #include "common/fs/path_util.h" | ||
| 25 | #include "common/logging/backend.h" | ||
| 26 | #include "common/logging/log.h" | ||
| 27 | #include "common/scm_rev.h" | ||
| 28 | #include "common/settings.h" | ||
| 29 | #include "common/string_util.h" | ||
| 30 | #include "core/announce_multiplayer_session.h" | ||
| 31 | #include "core/core.h" | ||
| 32 | #include "network/network.h" | ||
| 33 | #include "network/room.h" | ||
| 34 | #include "network/verify_user.h" | ||
| 35 | |||
| 36 | #ifdef ENABLE_WEB_SERVICE | ||
| 37 | #include "web_service/verify_user_jwt.h" | ||
| 38 | #endif | ||
| 39 | |||
| 40 | #undef _UNICODE | ||
| 41 | #include <getopt.h> | ||
| 42 | #ifndef _MSC_VER | ||
| 43 | #include <unistd.h> | ||
| 44 | #endif | ||
| 45 | |||
| 46 | static void PrintHelp(const char* argv0) { | ||
| 47 | LOG_INFO(Network, | ||
| 48 | "Usage: {}" | ||
| 49 | " [options] <filename>\n" | ||
| 50 | "--room-name The name of the room\n" | ||
| 51 | "--room-description The room description\n" | ||
| 52 | "--port The port used for the room\n" | ||
| 53 | "--max_members The maximum number of players for this room\n" | ||
| 54 | "--password The password for the room\n" | ||
| 55 | "--preferred-game The preferred game for this room\n" | ||
| 56 | "--preferred-game-id The preferred game-id for this room\n" | ||
| 57 | "--username The username used for announce\n" | ||
| 58 | "--token The token used for announce\n" | ||
| 59 | "--web-api-url yuzu Web API url\n" | ||
| 60 | "--ban-list-file The file for storing the room ban list\n" | ||
| 61 | "--log-file The file for storing the room log\n" | ||
| 62 | "--enable-yuzu-mods Allow yuzu Community Moderators to moderate on your room\n" | ||
| 63 | "-h, --help Display this help and exit\n" | ||
| 64 | "-v, --version Output version information and exit\n", | ||
| 65 | argv0); | ||
| 66 | } | ||
| 67 | |||
| 68 | static void PrintVersion() { | ||
| 69 | LOG_INFO(Network, "yuzu dedicated room {} {} Libnetwork: {}", Common::g_scm_branch, | ||
| 70 | Common::g_scm_desc, Network::network_version); | ||
| 71 | } | ||
| 72 | |||
| 73 | /// The magic text at the beginning of a yuzu-room ban list file. | ||
| 74 | static constexpr char BanListMagic[] = "YuzuRoom-BanList-1"; | ||
| 75 | |||
| 76 | static constexpr char token_delimiter{':'}; | ||
| 77 | |||
| 78 | static std::string UsernameFromDisplayToken(const std::string& display_token) { | ||
| 79 | std::size_t outlen; | ||
| 80 | |||
| 81 | std::array<unsigned char, 512> output{}; | ||
| 82 | mbedtls_base64_decode(output.data(), output.size(), &outlen, | ||
| 83 | reinterpret_cast<const unsigned char*>(display_token.c_str()), | ||
| 84 | display_token.length()); | ||
| 85 | std::string decoded_display_token(reinterpret_cast<char*>(&output), outlen); | ||
| 86 | return decoded_display_token.substr(0, decoded_display_token.find(token_delimiter)); | ||
| 87 | } | ||
| 88 | |||
| 89 | static std::string TokenFromDisplayToken(const std::string& display_token) { | ||
| 90 | std::size_t outlen; | ||
| 91 | |||
| 92 | std::array<unsigned char, 512> output{}; | ||
| 93 | mbedtls_base64_decode(output.data(), output.size(), &outlen, | ||
| 94 | reinterpret_cast<const unsigned char*>(display_token.c_str()), | ||
| 95 | display_token.length()); | ||
| 96 | std::string decoded_display_token(reinterpret_cast<char*>(&output), outlen); | ||
| 97 | return decoded_display_token.substr(decoded_display_token.find(token_delimiter) + 1); | ||
| 98 | } | ||
| 99 | |||
| 100 | static Network::Room::BanList LoadBanList(const std::string& path) { | ||
| 101 | std::ifstream file; | ||
| 102 | Common::FS::OpenFileStream(file, path, std::ios_base::in); | ||
| 103 | if (!file || file.eof()) { | ||
| 104 | LOG_ERROR(Network, "Could not open ban list!"); | ||
| 105 | return {}; | ||
| 106 | } | ||
| 107 | std::string magic; | ||
| 108 | std::getline(file, magic); | ||
| 109 | if (magic != BanListMagic) { | ||
| 110 | LOG_ERROR(Network, "Ban list is not valid!"); | ||
| 111 | return {}; | ||
| 112 | } | ||
| 113 | |||
| 114 | // false = username ban list, true = ip ban list | ||
| 115 | bool ban_list_type = false; | ||
| 116 | Network::Room::UsernameBanList username_ban_list; | ||
| 117 | Network::Room::IPBanList ip_ban_list; | ||
| 118 | while (!file.eof()) { | ||
| 119 | std::string line; | ||
| 120 | std::getline(file, line); | ||
| 121 | line.erase(std::remove(line.begin(), line.end(), '\0'), line.end()); | ||
| 122 | line = Common::StripSpaces(line); | ||
| 123 | if (line.empty()) { | ||
| 124 | // An empty line marks start of the IP ban list | ||
| 125 | ban_list_type = true; | ||
| 126 | continue; | ||
| 127 | } | ||
| 128 | if (ban_list_type) { | ||
| 129 | ip_ban_list.emplace_back(line); | ||
| 130 | } else { | ||
| 131 | username_ban_list.emplace_back(line); | ||
| 132 | } | ||
| 133 | } | ||
| 134 | |||
| 135 | return {username_ban_list, ip_ban_list}; | ||
| 136 | } | ||
| 137 | |||
| 138 | static void SaveBanList(const Network::Room::BanList& ban_list, const std::string& path) { | ||
| 139 | std::ofstream file; | ||
| 140 | Common::FS::OpenFileStream(file, path, std::ios_base::out); | ||
| 141 | if (!file) { | ||
| 142 | LOG_ERROR(Network, "Could not save ban list!"); | ||
| 143 | return; | ||
| 144 | } | ||
| 145 | |||
| 146 | file << BanListMagic << "\n"; | ||
| 147 | |||
| 148 | // Username ban list | ||
| 149 | for (const auto& username : ban_list.first) { | ||
| 150 | file << username << "\n"; | ||
| 151 | } | ||
| 152 | file << "\n"; | ||
| 153 | |||
| 154 | // IP ban list | ||
| 155 | for (const auto& ip : ban_list.second) { | ||
| 156 | file << ip << "\n"; | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | static void InitializeLogging(const std::string& log_file) { | ||
| 161 | Common::Log::Initialize(); | ||
| 162 | Common::Log::SetColorConsoleBackendEnabled(true); | ||
| 163 | Common::Log::Start(); | ||
| 164 | } | ||
| 165 | |||
| 166 | /// Application entry point | ||
| 167 | int main(int argc, char** argv) { | ||
| 168 | Common::DetachedTasks detached_tasks; | ||
| 169 | int option_index = 0; | ||
| 170 | char* endarg; | ||
| 171 | |||
| 172 | std::string room_name; | ||
| 173 | std::string room_description; | ||
| 174 | std::string password; | ||
| 175 | std::string preferred_game; | ||
| 176 | std::string username; | ||
| 177 | std::string token; | ||
| 178 | std::string web_api_url; | ||
| 179 | std::string ban_list_file; | ||
| 180 | std::string log_file = "yuzu-room.log"; | ||
| 181 | u64 preferred_game_id = 0; | ||
| 182 | u32 port = Network::DefaultRoomPort; | ||
| 183 | u32 max_members = 16; | ||
| 184 | bool enable_yuzu_mods = false; | ||
| 185 | |||
| 186 | static struct option long_options[] = { | ||
| 187 | {"room-name", required_argument, 0, 'n'}, | ||
| 188 | {"room-description", required_argument, 0, 'd'}, | ||
| 189 | {"port", required_argument, 0, 'p'}, | ||
| 190 | {"max_members", required_argument, 0, 'm'}, | ||
| 191 | {"password", required_argument, 0, 'w'}, | ||
| 192 | {"preferred-game", required_argument, 0, 'g'}, | ||
| 193 | {"preferred-game-id", required_argument, 0, 'i'}, | ||
| 194 | {"username", optional_argument, 0, 'u'}, | ||
| 195 | {"token", required_argument, 0, 't'}, | ||
| 196 | {"web-api-url", required_argument, 0, 'a'}, | ||
| 197 | {"ban-list-file", required_argument, 0, 'b'}, | ||
| 198 | {"log-file", required_argument, 0, 'l'}, | ||
| 199 | {"enable-yuzu-mods", no_argument, 0, 'e'}, | ||
| 200 | {"help", no_argument, 0, 'h'}, | ||
| 201 | {"version", no_argument, 0, 'v'}, | ||
| 202 | {0, 0, 0, 0}, | ||
| 203 | }; | ||
| 204 | |||
| 205 | InitializeLogging(log_file); | ||
| 206 | |||
| 207 | while (optind < argc) { | ||
| 208 | int arg = getopt_long(argc, argv, "n:d:p:m:w:g:u:t:a:i:l:hv", long_options, &option_index); | ||
| 209 | if (arg != -1) { | ||
| 210 | switch (static_cast<char>(arg)) { | ||
| 211 | case 'n': | ||
| 212 | room_name.assign(optarg); | ||
| 213 | break; | ||
| 214 | case 'd': | ||
| 215 | room_description.assign(optarg); | ||
| 216 | break; | ||
| 217 | case 'p': | ||
| 218 | port = strtoul(optarg, &endarg, 0); | ||
| 219 | break; | ||
| 220 | case 'm': | ||
| 221 | max_members = strtoul(optarg, &endarg, 0); | ||
| 222 | break; | ||
| 223 | case 'w': | ||
| 224 | password.assign(optarg); | ||
| 225 | break; | ||
| 226 | case 'g': | ||
| 227 | preferred_game.assign(optarg); | ||
| 228 | break; | ||
| 229 | case 'i': | ||
| 230 | preferred_game_id = strtoull(optarg, &endarg, 16); | ||
| 231 | break; | ||
| 232 | case 'u': | ||
| 233 | username.assign(optarg); | ||
| 234 | break; | ||
| 235 | case 't': | ||
| 236 | token.assign(optarg); | ||
| 237 | break; | ||
| 238 | case 'a': | ||
| 239 | web_api_url.assign(optarg); | ||
| 240 | break; | ||
| 241 | case 'b': | ||
| 242 | ban_list_file.assign(optarg); | ||
| 243 | break; | ||
| 244 | case 'l': | ||
| 245 | log_file.assign(optarg); | ||
| 246 | break; | ||
| 247 | case 'e': | ||
| 248 | enable_yuzu_mods = true; | ||
| 249 | break; | ||
| 250 | case 'h': | ||
| 251 | PrintHelp(argv[0]); | ||
| 252 | return 0; | ||
| 253 | case 'v': | ||
| 254 | PrintVersion(); | ||
| 255 | return 0; | ||
| 256 | } | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | if (room_name.empty()) { | ||
| 261 | LOG_ERROR(Network, "Room name is empty!"); | ||
| 262 | PrintHelp(argv[0]); | ||
| 263 | return -1; | ||
| 264 | } | ||
| 265 | if (preferred_game.empty()) { | ||
| 266 | LOG_ERROR(Network, "Preferred game is empty!"); | ||
| 267 | PrintHelp(argv[0]); | ||
| 268 | return -1; | ||
| 269 | } | ||
| 270 | if (preferred_game_id == 0) { | ||
| 271 | LOG_ERROR(Network, | ||
| 272 | "preferred-game-id not set!\nThis should get set to allow users to find your " | ||
| 273 | "room.\nSet with --preferred-game-id id"); | ||
| 274 | } | ||
| 275 | if (max_members > Network::MaxConcurrentConnections || max_members < 2) { | ||
| 276 | LOG_ERROR(Network, "max_members needs to be in the range 2 - {}!", | ||
| 277 | Network::MaxConcurrentConnections); | ||
| 278 | PrintHelp(argv[0]); | ||
| 279 | return -1; | ||
| 280 | } | ||
| 281 | if (port > UINT16_MAX) { | ||
| 282 | LOG_ERROR(Network, "Port needs to be in the range 0 - 65535!"); | ||
| 283 | PrintHelp(argv[0]); | ||
| 284 | return -1; | ||
| 285 | } | ||
| 286 | if (ban_list_file.empty()) { | ||
| 287 | LOG_ERROR(Network, "Ban list file not set!\nThis should get set to load and save room ban " | ||
| 288 | "list.\nSet with --ban-list-file <file>"); | ||
| 289 | } | ||
| 290 | bool announce = true; | ||
| 291 | if (token.empty() && announce) { | ||
| 292 | announce = false; | ||
| 293 | LOG_INFO(Network, "Token is empty: Hosting a private room"); | ||
| 294 | } | ||
| 295 | if (web_api_url.empty() && announce) { | ||
| 296 | announce = false; | ||
| 297 | LOG_INFO(Network, "Endpoint url is empty: Hosting a private room"); | ||
| 298 | } | ||
| 299 | if (announce) { | ||
| 300 | if (username.empty()) { | ||
| 301 | LOG_INFO(Network, "Hosting a public room"); | ||
| 302 | Settings::values.web_api_url = web_api_url; | ||
| 303 | Settings::values.yuzu_username = UsernameFromDisplayToken(token); | ||
| 304 | username = Settings::values.yuzu_username.GetValue(); | ||
| 305 | Settings::values.yuzu_token = TokenFromDisplayToken(token); | ||
| 306 | } else { | ||
| 307 | LOG_INFO(Network, "Hosting a public room"); | ||
| 308 | Settings::values.web_api_url = web_api_url; | ||
| 309 | Settings::values.yuzu_username = username; | ||
| 310 | Settings::values.yuzu_token = token; | ||
| 311 | } | ||
| 312 | } | ||
| 313 | if (!announce && enable_yuzu_mods) { | ||
| 314 | enable_yuzu_mods = false; | ||
| 315 | LOG_INFO(Network, "Can not enable yuzu Moderators for private rooms"); | ||
| 316 | } | ||
| 317 | |||
| 318 | // Load the ban list | ||
| 319 | Network::Room::BanList ban_list; | ||
| 320 | if (!ban_list_file.empty()) { | ||
| 321 | ban_list = LoadBanList(ban_list_file); | ||
| 322 | } | ||
| 323 | |||
| 324 | std::unique_ptr<Network::VerifyUser::Backend> verify_backend; | ||
| 325 | if (announce) { | ||
| 326 | #ifdef ENABLE_WEB_SERVICE | ||
| 327 | verify_backend = | ||
| 328 | std::make_unique<WebService::VerifyUserJWT>(Settings::values.web_api_url.GetValue()); | ||
| 329 | #else | ||
| 330 | LOG_INFO(Network, | ||
| 331 | "yuzu Web Services is not available with this build: validation is disabled."); | ||
| 332 | verify_backend = std::make_unique<Network::VerifyUser::NullBackend>(); | ||
| 333 | #endif | ||
| 334 | } else { | ||
| 335 | verify_backend = std::make_unique<Network::VerifyUser::NullBackend>(); | ||
| 336 | } | ||
| 337 | |||
| 338 | Network::RoomNetwork network{}; | ||
| 339 | network.Init(); | ||
| 340 | if (auto room = network.GetRoom().lock()) { | ||
| 341 | AnnounceMultiplayerRoom::GameInfo preferred_game_info{.name = preferred_game, | ||
| 342 | .id = preferred_game_id}; | ||
| 343 | if (!room->Create(room_name, room_description, "", port, password, max_members, username, | ||
| 344 | preferred_game_info, std::move(verify_backend), ban_list, | ||
| 345 | enable_yuzu_mods)) { | ||
| 346 | LOG_INFO(Network, "Failed to create room: "); | ||
| 347 | return -1; | ||
| 348 | } | ||
| 349 | LOG_INFO(Network, "Room is open. Close with Q+Enter..."); | ||
| 350 | auto announce_session = std::make_unique<Core::AnnounceMultiplayerSession>(network); | ||
| 351 | if (announce) { | ||
| 352 | announce_session->Start(); | ||
| 353 | } | ||
| 354 | while (room->GetState() == Network::Room::State::Open) { | ||
| 355 | std::string in; | ||
| 356 | std::cin >> in; | ||
| 357 | if (in.size() > 0) { | ||
| 358 | break; | ||
| 359 | } | ||
| 360 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); | ||
| 361 | } | ||
| 362 | if (announce) { | ||
| 363 | announce_session->Stop(); | ||
| 364 | } | ||
| 365 | announce_session.reset(); | ||
| 366 | // Save the ban list | ||
| 367 | if (!ban_list_file.empty()) { | ||
| 368 | SaveBanList(room->GetBanList(), ban_list_file); | ||
| 369 | } | ||
| 370 | room->Destroy(); | ||
| 371 | } | ||
| 372 | network.Shutdown(); | ||
| 373 | detached_tasks.WaitForAllTasks(); | ||
| 374 | return 0; | ||
| 375 | } | ||
diff --git a/src/dedicated_room/yuzu_room.rc b/src/dedicated_room/yuzu_room.rc new file mode 100644 index 000000000..a08957684 --- /dev/null +++ b/src/dedicated_room/yuzu_room.rc | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2017 Citra Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "winresrc.h" | ||
| 5 | ///////////////////////////////////////////////////////////////////////////// | ||
| 6 | // | ||
| 7 | // Icon | ||
| 8 | // | ||
| 9 | |||
| 10 | // Icon with lowest ID value placed first to ensure application icon | ||
| 11 | // remains consistent on all systems. | ||
| 12 | YUZU_ICON ICON "../../dist/yuzu.ico" | ||
| 13 | |||
| 14 | |||
| 15 | ///////////////////////////////////////////////////////////////////////////// | ||
| 16 | // | ||
| 17 | // RT_MANIFEST | ||
| 18 | // | ||
| 19 | |||
| 20 | 0 RT_MANIFEST "../../dist/yuzu.manifest" | ||
diff --git a/src/network/room.cpp b/src/network/room.cpp index 3fc3a0383..b06797bf1 100644 --- a/src/network/room.cpp +++ b/src/network/room.cpp | |||
| @@ -20,9 +20,7 @@ namespace Network { | |||
| 20 | 20 | ||
| 21 | class Room::RoomImpl { | 21 | class Room::RoomImpl { |
| 22 | public: | 22 | public: |
| 23 | // This MAC address is used to generate a 'Nintendo' like Mac address. | 23 | std::mt19937 random_gen; ///< Random number generator. Used for GenerateFakeIPAddress |
| 24 | const MacAddress NintendoOUI; | ||
| 25 | std::mt19937 random_gen; ///< Random number generator. Used for GenerateMacAddress | ||
| 26 | 24 | ||
| 27 | ENetHost* server = nullptr; ///< Network interface. | 25 | ENetHost* server = nullptr; ///< Network interface. |
| 28 | 26 | ||
| @@ -35,10 +33,9 @@ public: | |||
| 35 | std::string password; ///< The password required to connect to this room. | 33 | std::string password; ///< The password required to connect to this room. |
| 36 | 34 | ||
| 37 | struct Member { | 35 | struct Member { |
| 38 | std::string nickname; ///< The nickname of the member. | 36 | std::string nickname; ///< The nickname of the member. |
| 39 | std::string console_id_hash; ///< A hash of the console ID of the member. | 37 | GameInfo game_info; ///< The current game of the member |
| 40 | GameInfo game_info; ///< The current game of the member | 38 | IPv4Address fake_ip; ///< The assigned fake ip address of the member. |
| 41 | MacAddress mac_address; ///< The assigned mac address of the member. | ||
| 42 | /// Data of the user, often including authenticated forum username. | 39 | /// Data of the user, often including authenticated forum username. |
| 43 | VerifyUser::UserData user_data; | 40 | VerifyUser::UserData user_data; |
| 44 | ENetPeer* peer; ///< The remote peer. | 41 | ENetPeer* peer; ///< The remote peer. |
| @@ -51,8 +48,7 @@ public: | |||
| 51 | IPBanList ip_ban_list; ///< List of banned IP addresses | 48 | IPBanList ip_ban_list; ///< List of banned IP addresses |
| 52 | mutable std::mutex ban_list_mutex; ///< Mutex for the ban lists | 49 | mutable std::mutex ban_list_mutex; ///< Mutex for the ban lists |
| 53 | 50 | ||
| 54 | RoomImpl() | 51 | RoomImpl() : random_gen(std::random_device()()) {} |
| 55 | : NintendoOUI{0x00, 0x1F, 0x32, 0x00, 0x00, 0x00}, random_gen(std::random_device()()) {} | ||
| 56 | 52 | ||
| 57 | /// Thread that receives and dispatches network packets | 53 | /// Thread that receives and dispatches network packets |
| 58 | std::unique_ptr<std::thread> room_thread; | 54 | std::unique_ptr<std::thread> room_thread; |
| @@ -101,16 +97,10 @@ public: | |||
| 101 | bool IsValidNickname(const std::string& nickname) const; | 97 | bool IsValidNickname(const std::string& nickname) const; |
| 102 | 98 | ||
| 103 | /** | 99 | /** |
| 104 | * Returns whether the MAC address is valid, ie. isn't already taken by someone else in the | 100 | * Returns whether the fake ip address is valid, ie. isn't already taken by someone else in the |
| 105 | * room. | 101 | * room. |
| 106 | */ | 102 | */ |
| 107 | bool IsValidMacAddress(const MacAddress& address) const; | 103 | bool IsValidFakeIPAddress(const IPv4Address& address) const; |
| 108 | |||
| 109 | /** | ||
| 110 | * Returns whether the console ID (hash) is valid, ie. isn't already taken by someone else in | ||
| 111 | * the room. | ||
| 112 | */ | ||
| 113 | bool IsValidConsoleId(const std::string& console_id_hash) const; | ||
| 114 | 104 | ||
| 115 | /** | 105 | /** |
| 116 | * Returns whether a user has mod permissions. | 106 | * Returns whether a user has mod permissions. |
| @@ -128,15 +118,9 @@ public: | |||
| 128 | void SendNameCollision(ENetPeer* client); | 118 | void SendNameCollision(ENetPeer* client); |
| 129 | 119 | ||
| 130 | /** | 120 | /** |
| 131 | * Sends a ID_ROOM_MAC_COLLISION message telling the client that the MAC is invalid. | 121 | * Sends a ID_ROOM_IP_COLLISION message telling the client that the IP is invalid. |
| 132 | */ | 122 | */ |
| 133 | void SendMacCollision(ENetPeer* client); | 123 | void SendIPCollision(ENetPeer* client); |
| 134 | |||
| 135 | /** | ||
| 136 | * Sends a IdConsoleIdCollison message telling the client that another member with the same | ||
| 137 | * console ID exists. | ||
| 138 | */ | ||
| 139 | void SendConsoleIdCollision(ENetPeer* client); | ||
| 140 | 124 | ||
| 141 | /** | 125 | /** |
| 142 | * Sends a ID_ROOM_VERSION_MISMATCH message telling the client that the version is invalid. | 126 | * Sends a ID_ROOM_VERSION_MISMATCH message telling the client that the version is invalid. |
| @@ -152,13 +136,13 @@ public: | |||
| 152 | * Notifies the member that its connection attempt was successful, | 136 | * Notifies the member that its connection attempt was successful, |
| 153 | * and it is now part of the room. | 137 | * and it is now part of the room. |
| 154 | */ | 138 | */ |
| 155 | void SendJoinSuccess(ENetPeer* client, MacAddress mac_address); | 139 | void SendJoinSuccess(ENetPeer* client, IPv4Address fake_ip); |
| 156 | 140 | ||
| 157 | /** | 141 | /** |
| 158 | * Notifies the member that its connection attempt was successful, | 142 | * Notifies the member that its connection attempt was successful, |
| 159 | * and it is now part of the room, and it has been granted mod permissions. | 143 | * and it is now part of the room, and it has been granted mod permissions. |
| 160 | */ | 144 | */ |
| 161 | void SendJoinSuccessAsMod(ENetPeer* client, MacAddress mac_address); | 145 | void SendJoinSuccessAsMod(ENetPeer* client, IPv4Address fake_ip); |
| 162 | 146 | ||
| 163 | /** | 147 | /** |
| 164 | * Sends a IdHostKicked message telling the client that they have been kicked. | 148 | * Sends a IdHostKicked message telling the client that they have been kicked. |
| @@ -210,7 +194,7 @@ public: | |||
| 210 | * <u32> num_members: the number of currently joined clients | 194 | * <u32> num_members: the number of currently joined clients |
| 211 | * This is followed by the following three values for each member: | 195 | * This is followed by the following three values for each member: |
| 212 | * <String> nickname of that member | 196 | * <String> nickname of that member |
| 213 | * <MacAddress> mac_address of that member | 197 | * <IPv4Address> fake_ip of that member |
| 214 | * <String> game_name of that member | 198 | * <String> game_name of that member |
| 215 | */ | 199 | */ |
| 216 | void BroadcastRoomInformation(); | 200 | void BroadcastRoomInformation(); |
| @@ -219,13 +203,13 @@ public: | |||
| 219 | * Generates a free MAC address to assign to a new client. | 203 | * Generates a free MAC address to assign to a new client. |
| 220 | * The first 3 bytes are the NintendoOUI 0x00, 0x1F, 0x32 | 204 | * The first 3 bytes are the NintendoOUI 0x00, 0x1F, 0x32 |
| 221 | */ | 205 | */ |
| 222 | MacAddress GenerateMacAddress(); | 206 | IPv4Address GenerateFakeIPAddress(); |
| 223 | 207 | ||
| 224 | /** | 208 | /** |
| 225 | * Broadcasts this packet to all members except the sender. | 209 | * Broadcasts this packet to all members except the sender. |
| 226 | * @param event The ENet event containing the data | 210 | * @param event The ENet event containing the data |
| 227 | */ | 211 | */ |
| 228 | void HandleWifiPacket(const ENetEvent* event); | 212 | void HandleProxyPacket(const ENetEvent* event); |
| 229 | 213 | ||
| 230 | /** | 214 | /** |
| 231 | * Extracts a chat entry from a received ENet packet and adds it to the chat queue. | 215 | * Extracts a chat entry from a received ENet packet and adds it to the chat queue. |
| @@ -250,7 +234,7 @@ public: | |||
| 250 | void Room::RoomImpl::ServerLoop() { | 234 | void Room::RoomImpl::ServerLoop() { |
| 251 | while (state != State::Closed) { | 235 | while (state != State::Closed) { |
| 252 | ENetEvent event; | 236 | ENetEvent event; |
| 253 | if (enet_host_service(server, &event, 16) > 0) { | 237 | if (enet_host_service(server, &event, 50) > 0) { |
| 254 | switch (event.type) { | 238 | switch (event.type) { |
| 255 | case ENET_EVENT_TYPE_RECEIVE: | 239 | case ENET_EVENT_TYPE_RECEIVE: |
| 256 | switch (event.packet->data[0]) { | 240 | switch (event.packet->data[0]) { |
| @@ -260,8 +244,8 @@ void Room::RoomImpl::ServerLoop() { | |||
| 260 | case IdSetGameInfo: | 244 | case IdSetGameInfo: |
| 261 | HandleGameNamePacket(&event); | 245 | HandleGameNamePacket(&event); |
| 262 | break; | 246 | break; |
| 263 | case IdWifiPacket: | 247 | case IdProxyPacket: |
| 264 | HandleWifiPacket(&event); | 248 | HandleProxyPacket(&event); |
| 265 | break; | 249 | break; |
| 266 | case IdChatMessage: | 250 | case IdChatMessage: |
| 267 | HandleChatPacket(&event); | 251 | HandleChatPacket(&event); |
| @@ -313,11 +297,8 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) { | |||
| 313 | std::string nickname; | 297 | std::string nickname; |
| 314 | packet.Read(nickname); | 298 | packet.Read(nickname); |
| 315 | 299 | ||
| 316 | std::string console_id_hash; | 300 | IPv4Address preferred_fake_ip; |
| 317 | packet.Read(console_id_hash); | 301 | packet.Read(preferred_fake_ip); |
| 318 | |||
| 319 | MacAddress preferred_mac; | ||
| 320 | packet.Read(preferred_mac); | ||
| 321 | 302 | ||
| 322 | u32 client_version; | 303 | u32 client_version; |
| 323 | packet.Read(client_version); | 304 | packet.Read(client_version); |
| @@ -338,20 +319,15 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) { | |||
| 338 | return; | 319 | return; |
| 339 | } | 320 | } |
| 340 | 321 | ||
| 341 | if (preferred_mac != NoPreferredMac) { | 322 | if (preferred_fake_ip != NoPreferredIP) { |
| 342 | // Verify if the preferred mac is available | 323 | // Verify if the preferred fake ip is available |
| 343 | if (!IsValidMacAddress(preferred_mac)) { | 324 | if (!IsValidFakeIPAddress(preferred_fake_ip)) { |
| 344 | SendMacCollision(event->peer); | 325 | SendIPCollision(event->peer); |
| 345 | return; | 326 | return; |
| 346 | } | 327 | } |
| 347 | } else { | 328 | } else { |
| 348 | // Assign a MAC address of this client automatically | 329 | // Assign a fake ip address of this client automatically |
| 349 | preferred_mac = GenerateMacAddress(); | 330 | preferred_fake_ip = GenerateFakeIPAddress(); |
| 350 | } | ||
| 351 | |||
| 352 | if (!IsValidConsoleId(console_id_hash)) { | ||
| 353 | SendConsoleIdCollision(event->peer); | ||
| 354 | return; | ||
| 355 | } | 331 | } |
| 356 | 332 | ||
| 357 | if (client_version != network_version) { | 333 | if (client_version != network_version) { |
| @@ -361,8 +337,7 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) { | |||
| 361 | 337 | ||
| 362 | // At this point the client is ready to be added to the room. | 338 | // At this point the client is ready to be added to the room. |
| 363 | Member member{}; | 339 | Member member{}; |
| 364 | member.mac_address = preferred_mac; | 340 | member.fake_ip = preferred_fake_ip; |
| 365 | member.console_id_hash = console_id_hash; | ||
| 366 | member.nickname = nickname; | 341 | member.nickname = nickname; |
| 367 | member.peer = event->peer; | 342 | member.peer = event->peer; |
| 368 | 343 | ||
| @@ -408,9 +383,9 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) { | |||
| 408 | // Notify everyone that the room information has changed. | 383 | // Notify everyone that the room information has changed. |
| 409 | BroadcastRoomInformation(); | 384 | BroadcastRoomInformation(); |
| 410 | if (HasModPermission(event->peer)) { | 385 | if (HasModPermission(event->peer)) { |
| 411 | SendJoinSuccessAsMod(event->peer, preferred_mac); | 386 | SendJoinSuccessAsMod(event->peer, preferred_fake_ip); |
| 412 | } else { | 387 | } else { |
| 413 | SendJoinSuccess(event->peer, preferred_mac); | 388 | SendJoinSuccess(event->peer, preferred_fake_ip); |
| 414 | } | 389 | } |
| 415 | } | 390 | } |
| 416 | 391 | ||
| @@ -575,19 +550,11 @@ bool Room::RoomImpl::IsValidNickname(const std::string& nickname) const { | |||
| 575 | [&nickname](const auto& member) { return member.nickname != nickname; }); | 550 | [&nickname](const auto& member) { return member.nickname != nickname; }); |
| 576 | } | 551 | } |
| 577 | 552 | ||
| 578 | bool Room::RoomImpl::IsValidMacAddress(const MacAddress& address) const { | 553 | bool Room::RoomImpl::IsValidFakeIPAddress(const IPv4Address& address) const { |
| 579 | // A MAC address is valid if it is not already taken by anybody else in the room. | 554 | // An IP address is valid if it is not already taken by anybody else in the room. |
| 580 | std::lock_guard lock(member_mutex); | 555 | std::lock_guard lock(member_mutex); |
| 581 | return std::all_of(members.begin(), members.end(), | 556 | return std::all_of(members.begin(), members.end(), |
| 582 | [&address](const auto& member) { return member.mac_address != address; }); | 557 | [&address](const auto& member) { return member.fake_ip != address; }); |
| 583 | } | ||
| 584 | |||
| 585 | bool Room::RoomImpl::IsValidConsoleId(const std::string& console_id_hash) const { | ||
| 586 | // A Console ID is valid if it is not already taken by anybody else in the room. | ||
| 587 | std::lock_guard lock(member_mutex); | ||
| 588 | return std::all_of(members.begin(), members.end(), [&console_id_hash](const auto& member) { | ||
| 589 | return member.console_id_hash != console_id_hash; | ||
| 590 | }); | ||
| 591 | } | 558 | } |
| 592 | 559 | ||
| 593 | bool Room::RoomImpl::HasModPermission(const ENetPeer* client) const { | 560 | bool Room::RoomImpl::HasModPermission(const ENetPeer* client) const { |
| @@ -621,19 +588,9 @@ void Room::RoomImpl::SendNameCollision(ENetPeer* client) { | |||
| 621 | enet_host_flush(server); | 588 | enet_host_flush(server); |
| 622 | } | 589 | } |
| 623 | 590 | ||
| 624 | void Room::RoomImpl::SendMacCollision(ENetPeer* client) { | 591 | void Room::RoomImpl::SendIPCollision(ENetPeer* client) { |
| 625 | Packet packet; | 592 | Packet packet; |
| 626 | packet.Write(static_cast<u8>(IdMacCollision)); | 593 | packet.Write(static_cast<u8>(IdIpCollision)); |
| 627 | |||
| 628 | ENetPacket* enet_packet = | ||
| 629 | enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); | ||
| 630 | enet_peer_send(client, 0, enet_packet); | ||
| 631 | enet_host_flush(server); | ||
| 632 | } | ||
| 633 | |||
| 634 | void Room::RoomImpl::SendConsoleIdCollision(ENetPeer* client) { | ||
| 635 | Packet packet; | ||
| 636 | packet.Write(static_cast<u8>(IdConsoleIdCollision)); | ||
| 637 | 594 | ||
| 638 | ENetPacket* enet_packet = | 595 | ENetPacket* enet_packet = |
| 639 | enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); | 596 | enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); |
| @@ -672,20 +629,20 @@ void Room::RoomImpl::SendVersionMismatch(ENetPeer* client) { | |||
| 672 | enet_host_flush(server); | 629 | enet_host_flush(server); |
| 673 | } | 630 | } |
| 674 | 631 | ||
| 675 | void Room::RoomImpl::SendJoinSuccess(ENetPeer* client, MacAddress mac_address) { | 632 | void Room::RoomImpl::SendJoinSuccess(ENetPeer* client, IPv4Address fake_ip) { |
| 676 | Packet packet; | 633 | Packet packet; |
| 677 | packet.Write(static_cast<u8>(IdJoinSuccess)); | 634 | packet.Write(static_cast<u8>(IdJoinSuccess)); |
| 678 | packet.Write(mac_address); | 635 | packet.Write(fake_ip); |
| 679 | ENetPacket* enet_packet = | 636 | ENetPacket* enet_packet = |
| 680 | enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); | 637 | enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); |
| 681 | enet_peer_send(client, 0, enet_packet); | 638 | enet_peer_send(client, 0, enet_packet); |
| 682 | enet_host_flush(server); | 639 | enet_host_flush(server); |
| 683 | } | 640 | } |
| 684 | 641 | ||
| 685 | void Room::RoomImpl::SendJoinSuccessAsMod(ENetPeer* client, MacAddress mac_address) { | 642 | void Room::RoomImpl::SendJoinSuccessAsMod(ENetPeer* client, IPv4Address fake_ip) { |
| 686 | Packet packet; | 643 | Packet packet; |
| 687 | packet.Write(static_cast<u8>(IdJoinSuccessAsMod)); | 644 | packet.Write(static_cast<u8>(IdJoinSuccessAsMod)); |
| 688 | packet.Write(mac_address); | 645 | packet.Write(fake_ip); |
| 689 | ENetPacket* enet_packet = | 646 | ENetPacket* enet_packet = |
| 690 | enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); | 647 | enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); |
| 691 | enet_peer_send(client, 0, enet_packet); | 648 | enet_peer_send(client, 0, enet_packet); |
| @@ -818,7 +775,7 @@ void Room::RoomImpl::BroadcastRoomInformation() { | |||
| 818 | std::lock_guard lock(member_mutex); | 775 | std::lock_guard lock(member_mutex); |
| 819 | for (const auto& member : members) { | 776 | for (const auto& member : members) { |
| 820 | packet.Write(member.nickname); | 777 | packet.Write(member.nickname); |
| 821 | packet.Write(member.mac_address); | 778 | packet.Write(member.fake_ip); |
| 822 | packet.Write(member.game_info.name); | 779 | packet.Write(member.game_info.name); |
| 823 | packet.Write(member.game_info.id); | 780 | packet.Write(member.game_info.id); |
| 824 | packet.Write(member.user_data.username); | 781 | packet.Write(member.user_data.username); |
| @@ -833,34 +790,43 @@ void Room::RoomImpl::BroadcastRoomInformation() { | |||
| 833 | enet_host_flush(server); | 790 | enet_host_flush(server); |
| 834 | } | 791 | } |
| 835 | 792 | ||
| 836 | MacAddress Room::RoomImpl::GenerateMacAddress() { | 793 | IPv4Address Room::RoomImpl::GenerateFakeIPAddress() { |
| 837 | MacAddress result_mac = | 794 | IPv4Address result_ip{192, 168, 0, 0}; |
| 838 | NintendoOUI; // The first three bytes of each MAC address will be the NintendoOUI | 795 | std::uniform_int_distribution<> dis(0x01, 0xFE); // Random byte between 1 and 0xFE |
| 839 | std::uniform_int_distribution<> dis(0x00, 0xFF); // Random byte between 0 and 0xFF | ||
| 840 | do { | 796 | do { |
| 841 | for (std::size_t i = 3; i < result_mac.size(); ++i) { | 797 | for (std::size_t i = 2; i < result_ip.size(); ++i) { |
| 842 | result_mac[i] = dis(random_gen); | 798 | result_ip[i] = dis(random_gen); |
| 843 | } | 799 | } |
| 844 | } while (!IsValidMacAddress(result_mac)); | 800 | } while (!IsValidFakeIPAddress(result_ip)); |
| 845 | return result_mac; | 801 | |
| 802 | return result_ip; | ||
| 846 | } | 803 | } |
| 847 | 804 | ||
| 848 | void Room::RoomImpl::HandleWifiPacket(const ENetEvent* event) { | 805 | void Room::RoomImpl::HandleProxyPacket(const ENetEvent* event) { |
| 849 | Packet in_packet; | 806 | Packet in_packet; |
| 850 | in_packet.Append(event->packet->data, event->packet->dataLength); | 807 | in_packet.Append(event->packet->data, event->packet->dataLength); |
| 851 | in_packet.IgnoreBytes(sizeof(u8)); // Message type | 808 | in_packet.IgnoreBytes(sizeof(u8)); // Message type |
| 852 | in_packet.IgnoreBytes(sizeof(u8)); // WifiPacket Type | 809 | |
| 853 | in_packet.IgnoreBytes(sizeof(u8)); // WifiPacket Channel | 810 | in_packet.IgnoreBytes(sizeof(u8)); // Domain |
| 854 | in_packet.IgnoreBytes(sizeof(MacAddress)); // WifiPacket Transmitter Address | 811 | in_packet.IgnoreBytes(sizeof(IPv4Address)); // IP |
| 855 | MacAddress destination_address; | 812 | in_packet.IgnoreBytes(sizeof(u16)); // Port |
| 856 | in_packet.Read(destination_address); | 813 | |
| 814 | in_packet.IgnoreBytes(sizeof(u8)); // Domain | ||
| 815 | IPv4Address remote_ip; | ||
| 816 | in_packet.Read(remote_ip); // IP | ||
| 817 | in_packet.IgnoreBytes(sizeof(u16)); // Port | ||
| 818 | |||
| 819 | in_packet.IgnoreBytes(sizeof(u8)); // Protocol | ||
| 820 | bool broadcast; | ||
| 821 | in_packet.Read(broadcast); // Broadcast | ||
| 857 | 822 | ||
| 858 | Packet out_packet; | 823 | Packet out_packet; |
| 859 | out_packet.Append(event->packet->data, event->packet->dataLength); | 824 | out_packet.Append(event->packet->data, event->packet->dataLength); |
| 860 | ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(), | 825 | ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(), |
| 861 | ENET_PACKET_FLAG_RELIABLE); | 826 | ENET_PACKET_FLAG_RELIABLE); |
| 862 | 827 | ||
| 863 | if (destination_address == BroadcastMac) { // Send the data to everyone except the sender | 828 | const auto& destination_address = remote_ip; |
| 829 | if (broadcast) { // Send the data to everyone except the sender | ||
| 864 | std::lock_guard lock(member_mutex); | 830 | std::lock_guard lock(member_mutex); |
| 865 | bool sent_packet = false; | 831 | bool sent_packet = false; |
| 866 | for (const auto& member : members) { | 832 | for (const auto& member : members) { |
| @@ -877,16 +843,16 @@ void Room::RoomImpl::HandleWifiPacket(const ENetEvent* event) { | |||
| 877 | std::lock_guard lock(member_mutex); | 843 | std::lock_guard lock(member_mutex); |
| 878 | auto member = std::find_if(members.begin(), members.end(), | 844 | auto member = std::find_if(members.begin(), members.end(), |
| 879 | [destination_address](const Member& member_entry) -> bool { | 845 | [destination_address](const Member& member_entry) -> bool { |
| 880 | return member_entry.mac_address == destination_address; | 846 | return member_entry.fake_ip == destination_address; |
| 881 | }); | 847 | }); |
| 882 | if (member != members.end()) { | 848 | if (member != members.end()) { |
| 883 | enet_peer_send(member->peer, 0, enet_packet); | 849 | enet_peer_send(member->peer, 0, enet_packet); |
| 884 | } else { | 850 | } else { |
| 885 | LOG_ERROR(Network, | 851 | LOG_ERROR(Network, |
| 886 | "Attempting to send to unknown MAC address: " | 852 | "Attempting to send to unknown IP address: " |
| 887 | "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}", | 853 | "{}.{}.{}.{}", |
| 888 | destination_address[0], destination_address[1], destination_address[2], | 854 | destination_address[0], destination_address[1], destination_address[2], |
| 889 | destination_address[3], destination_address[4], destination_address[5]); | 855 | destination_address[3]); |
| 890 | enet_packet_destroy(enet_packet); | 856 | enet_packet_destroy(enet_packet); |
| 891 | } | 857 | } |
| 892 | } | 858 | } |
| @@ -1073,7 +1039,7 @@ std::vector<Member> Room::GetRoomMemberList() const { | |||
| 1073 | member.username = member_impl.user_data.username; | 1039 | member.username = member_impl.user_data.username; |
| 1074 | member.display_name = member_impl.user_data.display_name; | 1040 | member.display_name = member_impl.user_data.display_name; |
| 1075 | member.avatar_url = member_impl.user_data.avatar_url; | 1041 | member.avatar_url = member_impl.user_data.avatar_url; |
| 1076 | member.mac_address = member_impl.mac_address; | 1042 | member.fake_ip = member_impl.fake_ip; |
| 1077 | member.game = member_impl.game_info; | 1043 | member.game = member_impl.game_info; |
| 1078 | member_list.push_back(member); | 1044 | member_list.push_back(member); |
| 1079 | } | 1045 | } |
diff --git a/src/network/room.h b/src/network/room.h index 6f7e3b5b5..c2a4b1a70 100644 --- a/src/network/room.h +++ b/src/network/room.h | |||
| @@ -9,12 +9,12 @@ | |||
| 9 | #include <vector> | 9 | #include <vector> |
| 10 | #include "common/announce_multiplayer_room.h" | 10 | #include "common/announce_multiplayer_room.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/socket_types.h" | ||
| 12 | #include "network/verify_user.h" | 13 | #include "network/verify_user.h" |
| 13 | 14 | ||
| 14 | namespace Network { | 15 | namespace Network { |
| 15 | 16 | ||
| 16 | using AnnounceMultiplayerRoom::GameInfo; | 17 | using AnnounceMultiplayerRoom::GameInfo; |
| 17 | using AnnounceMultiplayerRoom::MacAddress; | ||
| 18 | using AnnounceMultiplayerRoom::Member; | 18 | using AnnounceMultiplayerRoom::Member; |
| 19 | using AnnounceMultiplayerRoom::RoomInformation; | 19 | using AnnounceMultiplayerRoom::RoomInformation; |
| 20 | 20 | ||
| @@ -29,12 +29,9 @@ static constexpr u32 MaxConcurrentConnections = 254; | |||
| 29 | 29 | ||
| 30 | constexpr std::size_t NumChannels = 1; // Number of channels used for the connection | 30 | constexpr std::size_t NumChannels = 1; // Number of channels used for the connection |
| 31 | 31 | ||
| 32 | /// A special MAC address that tells the room we're joining to assign us a MAC address | 32 | /// A special IP address that tells the room we're joining to assign us a IP address |
| 33 | /// automatically. | 33 | /// automatically. |
| 34 | constexpr MacAddress NoPreferredMac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; | 34 | constexpr IPv4Address NoPreferredIP = {0xFF, 0xFF, 0xFF, 0xFF}; |
| 35 | |||
| 36 | // 802.11 broadcast MAC address | ||
| 37 | constexpr MacAddress BroadcastMac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; | ||
| 38 | 35 | ||
| 39 | // The different types of messages that can be sent. The first byte of each packet defines the type | 36 | // The different types of messages that can be sent. The first byte of each packet defines the type |
| 40 | enum RoomMessageTypes : u8 { | 37 | enum RoomMessageTypes : u8 { |
| @@ -42,15 +39,14 @@ enum RoomMessageTypes : u8 { | |||
| 42 | IdJoinSuccess, | 39 | IdJoinSuccess, |
| 43 | IdRoomInformation, | 40 | IdRoomInformation, |
| 44 | IdSetGameInfo, | 41 | IdSetGameInfo, |
| 45 | IdWifiPacket, | 42 | IdProxyPacket, |
| 46 | IdChatMessage, | 43 | IdChatMessage, |
| 47 | IdNameCollision, | 44 | IdNameCollision, |
| 48 | IdMacCollision, | 45 | IdIpCollision, |
| 49 | IdVersionMismatch, | 46 | IdVersionMismatch, |
| 50 | IdWrongPassword, | 47 | IdWrongPassword, |
| 51 | IdCloseRoom, | 48 | IdCloseRoom, |
| 52 | IdRoomIsFull, | 49 | IdRoomIsFull, |
| 53 | IdConsoleIdCollision, | ||
| 54 | IdStatusMessage, | 50 | IdStatusMessage, |
| 55 | IdHostKicked, | 51 | IdHostKicked, |
| 56 | IdHostBanned, | 52 | IdHostBanned, |
diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp index e4f823e98..9f08bf611 100644 --- a/src/network/room_member.cpp +++ b/src/network/room_member.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <set> | 7 | #include <set> |
| 8 | #include <thread> | 8 | #include <thread> |
| 9 | #include "common/assert.h" | 9 | #include "common/assert.h" |
| 10 | #include "common/socket_types.h" | ||
| 10 | #include "enet/enet.h" | 11 | #include "enet/enet.h" |
| 11 | #include "network/packet.h" | 12 | #include "network/packet.h" |
| 12 | #include "network/room_member.h" | 13 | #include "network/room_member.h" |
| @@ -38,7 +39,7 @@ public: | |||
| 38 | std::string username; ///< The username of this member. | 39 | std::string username; ///< The username of this member. |
| 39 | mutable std::mutex username_mutex; ///< Mutex for locking username. | 40 | mutable std::mutex username_mutex; ///< Mutex for locking username. |
| 40 | 41 | ||
| 41 | MacAddress mac_address; ///< The mac_address of this member. | 42 | IPv4Address fake_ip; ///< The fake ip of this member. |
| 42 | 43 | ||
| 43 | std::mutex network_mutex; ///< Mutex that controls access to the `client` variable. | 44 | std::mutex network_mutex; ///< Mutex that controls access to the `client` variable. |
| 44 | /// Thread that receives and dispatches network packets | 45 | /// Thread that receives and dispatches network packets |
| @@ -56,7 +57,7 @@ public: | |||
| 56 | CallbackSet<T>& Get(); | 57 | CallbackSet<T>& Get(); |
| 57 | 58 | ||
| 58 | private: | 59 | private: |
| 59 | CallbackSet<WifiPacket> callback_set_wifi_packet; | 60 | CallbackSet<ProxyPacket> callback_set_proxy_packet; |
| 60 | CallbackSet<ChatEntry> callback_set_chat_messages; | 61 | CallbackSet<ChatEntry> callback_set_chat_messages; |
| 61 | CallbackSet<StatusMessageEntry> callback_set_status_messages; | 62 | CallbackSet<StatusMessageEntry> callback_set_status_messages; |
| 62 | CallbackSet<RoomInformation> callback_set_room_information; | 63 | CallbackSet<RoomInformation> callback_set_room_information; |
| @@ -78,15 +79,15 @@ public: | |||
| 78 | 79 | ||
| 79 | /** | 80 | /** |
| 80 | * Sends a request to the server, asking for permission to join a room with the specified | 81 | * Sends a request to the server, asking for permission to join a room with the specified |
| 81 | * nickname and preferred mac. | 82 | * nickname and preferred fake ip. |
| 82 | * @params nickname The desired nickname. | 83 | * @params nickname The desired nickname. |
| 83 | * @params console_id_hash A hash of the Console ID. | 84 | * @params preferred_fake_ip The preferred IP address to use in the room, the NoPreferredIP |
| 84 | * @params preferred_mac The preferred MAC address to use in the room, the NoPreferredMac tells | 85 | * tells |
| 85 | * @params password The password for the room | 86 | * @params password The password for the room |
| 86 | * the server to assign one for us. | 87 | * the server to assign one for us. |
| 87 | */ | 88 | */ |
| 88 | void SendJoinRequest(const std::string& nickname_, const std::string& console_id_hash, | 89 | void SendJoinRequest(const std::string& nickname_, |
| 89 | const MacAddress& preferred_mac = NoPreferredMac, | 90 | const IPv4Address& preferred_fake_ip = NoPreferredIP, |
| 90 | const std::string& password = "", const std::string& token = ""); | 91 | const std::string& password = "", const std::string& token = ""); |
| 91 | 92 | ||
| 92 | /** | 93 | /** |
| @@ -101,10 +102,10 @@ public: | |||
| 101 | void HandleRoomInformationPacket(const ENetEvent* event); | 102 | void HandleRoomInformationPacket(const ENetEvent* event); |
| 102 | 103 | ||
| 103 | /** | 104 | /** |
| 104 | * Extracts a WifiPacket from a received ENet packet. | 105 | * Extracts a ProxyPacket from a received ENet packet. |
| 105 | * @param event The ENet event that was received. | 106 | * @param event The ENet event that was received. |
| 106 | */ | 107 | */ |
| 107 | void HandleWifiPackets(const ENetEvent* event); | 108 | void HandleProxyPackets(const ENetEvent* event); |
| 108 | 109 | ||
| 109 | /** | 110 | /** |
| 110 | * Extracts a chat entry from a received ENet packet and adds it to the chat queue. | 111 | * Extracts a chat entry from a received ENet packet and adds it to the chat queue. |
| @@ -158,12 +159,12 @@ void RoomMember::RoomMemberImpl::MemberLoop() { | |||
| 158 | while (IsConnected()) { | 159 | while (IsConnected()) { |
| 159 | std::lock_guard lock(network_mutex); | 160 | std::lock_guard lock(network_mutex); |
| 160 | ENetEvent event; | 161 | ENetEvent event; |
| 161 | if (enet_host_service(client, &event, 16) > 0) { | 162 | if (enet_host_service(client, &event, 100) > 0) { |
| 162 | switch (event.type) { | 163 | switch (event.type) { |
| 163 | case ENET_EVENT_TYPE_RECEIVE: | 164 | case ENET_EVENT_TYPE_RECEIVE: |
| 164 | switch (event.packet->data[0]) { | 165 | switch (event.packet->data[0]) { |
| 165 | case IdWifiPacket: | 166 | case IdProxyPacket: |
| 166 | HandleWifiPackets(&event); | 167 | HandleProxyPackets(&event); |
| 167 | break; | 168 | break; |
| 168 | case IdChatMessage: | 169 | case IdChatMessage: |
| 169 | HandleChatPacket(&event); | 170 | HandleChatPacket(&event); |
| @@ -198,13 +199,9 @@ void RoomMember::RoomMemberImpl::MemberLoop() { | |||
| 198 | SetState(State::Idle); | 199 | SetState(State::Idle); |
| 199 | SetError(Error::NameCollision); | 200 | SetError(Error::NameCollision); |
| 200 | break; | 201 | break; |
| 201 | case IdMacCollision: | 202 | case IdIpCollision: |
| 202 | SetState(State::Idle); | 203 | SetState(State::Idle); |
| 203 | SetError(Error::MacCollision); | 204 | SetError(Error::IpCollision); |
| 204 | break; | ||
| 205 | case IdConsoleIdCollision: | ||
| 206 | SetState(State::Idle); | ||
| 207 | SetError(Error::ConsoleIdCollision); | ||
| 208 | break; | 205 | break; |
| 209 | case IdVersionMismatch: | 206 | case IdVersionMismatch: |
| 210 | SetState(State::Idle); | 207 | SetState(State::Idle); |
| @@ -275,15 +272,13 @@ void RoomMember::RoomMemberImpl::Send(Packet&& packet) { | |||
| 275 | } | 272 | } |
| 276 | 273 | ||
| 277 | void RoomMember::RoomMemberImpl::SendJoinRequest(const std::string& nickname_, | 274 | void RoomMember::RoomMemberImpl::SendJoinRequest(const std::string& nickname_, |
| 278 | const std::string& console_id_hash, | 275 | const IPv4Address& preferred_fake_ip, |
| 279 | const MacAddress& preferred_mac, | ||
| 280 | const std::string& password, | 276 | const std::string& password, |
| 281 | const std::string& token) { | 277 | const std::string& token) { |
| 282 | Packet packet; | 278 | Packet packet; |
| 283 | packet.Write(static_cast<u8>(IdJoinRequest)); | 279 | packet.Write(static_cast<u8>(IdJoinRequest)); |
| 284 | packet.Write(nickname_); | 280 | packet.Write(nickname_); |
| 285 | packet.Write(console_id_hash); | 281 | packet.Write(preferred_fake_ip); |
| 286 | packet.Write(preferred_mac); | ||
| 287 | packet.Write(network_version); | 282 | packet.Write(network_version); |
| 288 | packet.Write(password); | 283 | packet.Write(password); |
| 289 | packet.Write(token); | 284 | packet.Write(token); |
| @@ -317,7 +312,7 @@ void RoomMember::RoomMemberImpl::HandleRoomInformationPacket(const ENetEvent* ev | |||
| 317 | 312 | ||
| 318 | for (auto& member : member_information) { | 313 | for (auto& member : member_information) { |
| 319 | packet.Read(member.nickname); | 314 | packet.Read(member.nickname); |
| 320 | packet.Read(member.mac_address); | 315 | packet.Read(member.fake_ip); |
| 321 | packet.Read(member.game_info.name); | 316 | packet.Read(member.game_info.name); |
| 322 | packet.Read(member.game_info.id); | 317 | packet.Read(member.game_info.id); |
| 323 | packet.Read(member.username); | 318 | packet.Read(member.username); |
| @@ -342,29 +337,38 @@ void RoomMember::RoomMemberImpl::HandleJoinPacket(const ENetEvent* event) { | |||
| 342 | packet.IgnoreBytes(sizeof(u8)); // Ignore the message type | 337 | packet.IgnoreBytes(sizeof(u8)); // Ignore the message type |
| 343 | 338 | ||
| 344 | // Parse the MAC Address from the packet | 339 | // Parse the MAC Address from the packet |
| 345 | packet.Read(mac_address); | 340 | packet.Read(fake_ip); |
| 346 | } | 341 | } |
| 347 | 342 | ||
| 348 | void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) { | 343 | void RoomMember::RoomMemberImpl::HandleProxyPackets(const ENetEvent* event) { |
| 349 | WifiPacket wifi_packet{}; | 344 | ProxyPacket proxy_packet{}; |
| 350 | Packet packet; | 345 | Packet packet; |
| 351 | packet.Append(event->packet->data, event->packet->dataLength); | 346 | packet.Append(event->packet->data, event->packet->dataLength); |
| 352 | 347 | ||
| 353 | // Ignore the first byte, which is the message id. | 348 | // Ignore the first byte, which is the message id. |
| 354 | packet.IgnoreBytes(sizeof(u8)); // Ignore the message type | 349 | packet.IgnoreBytes(sizeof(u8)); // Ignore the message type |
| 355 | 350 | ||
| 356 | // Parse the WifiPacket from the packet | 351 | // Parse the ProxyPacket from the packet |
| 357 | u8 frame_type; | 352 | u8 local_family; |
| 358 | packet.Read(frame_type); | 353 | packet.Read(local_family); |
| 359 | WifiPacket::PacketType type = static_cast<WifiPacket::PacketType>(frame_type); | 354 | proxy_packet.local_endpoint.family = static_cast<Domain>(local_family); |
| 355 | packet.Read(proxy_packet.local_endpoint.ip); | ||
| 356 | packet.Read(proxy_packet.local_endpoint.portno); | ||
| 357 | |||
| 358 | u8 remote_family; | ||
| 359 | packet.Read(remote_family); | ||
| 360 | proxy_packet.remote_endpoint.family = static_cast<Domain>(remote_family); | ||
| 361 | packet.Read(proxy_packet.remote_endpoint.ip); | ||
| 362 | packet.Read(proxy_packet.remote_endpoint.portno); | ||
| 363 | |||
| 364 | u8 protocol_type; | ||
| 365 | packet.Read(protocol_type); | ||
| 366 | proxy_packet.protocol = static_cast<Protocol>(protocol_type); | ||
| 360 | 367 | ||
| 361 | wifi_packet.type = type; | 368 | packet.Read(proxy_packet.broadcast); |
| 362 | packet.Read(wifi_packet.channel); | 369 | packet.Read(proxy_packet.data); |
| 363 | packet.Read(wifi_packet.transmitter_address); | ||
| 364 | packet.Read(wifi_packet.destination_address); | ||
| 365 | packet.Read(wifi_packet.data); | ||
| 366 | 370 | ||
| 367 | Invoke<WifiPacket>(wifi_packet); | 371 | Invoke<ProxyPacket>(proxy_packet); |
| 368 | } | 372 | } |
| 369 | 373 | ||
| 370 | void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) { | 374 | void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) { |
| @@ -440,8 +444,8 @@ void RoomMember::RoomMemberImpl::Disconnect() { | |||
| 440 | } | 444 | } |
| 441 | 445 | ||
| 442 | template <> | 446 | template <> |
| 443 | RoomMember::RoomMemberImpl::CallbackSet<WifiPacket>& RoomMember::RoomMemberImpl::Callbacks::Get() { | 447 | RoomMember::RoomMemberImpl::CallbackSet<ProxyPacket>& RoomMember::RoomMemberImpl::Callbacks::Get() { |
| 444 | return callback_set_wifi_packet; | 448 | return callback_set_proxy_packet; |
| 445 | } | 449 | } |
| 446 | 450 | ||
| 447 | template <> | 451 | template <> |
| @@ -525,19 +529,18 @@ const std::string& RoomMember::GetUsername() const { | |||
| 525 | return room_member_impl->username; | 529 | return room_member_impl->username; |
| 526 | } | 530 | } |
| 527 | 531 | ||
| 528 | const MacAddress& RoomMember::GetMacAddress() const { | 532 | const IPv4Address& RoomMember::GetFakeIpAddress() const { |
| 529 | ASSERT_MSG(IsConnected(), "Tried to get MAC address while not connected"); | 533 | ASSERT_MSG(IsConnected(), "Tried to get fake ip address while not connected"); |
| 530 | return room_member_impl->mac_address; | 534 | return room_member_impl->fake_ip; |
| 531 | } | 535 | } |
| 532 | 536 | ||
| 533 | RoomInformation RoomMember::GetRoomInformation() const { | 537 | RoomInformation RoomMember::GetRoomInformation() const { |
| 534 | return room_member_impl->room_information; | 538 | return room_member_impl->room_information; |
| 535 | } | 539 | } |
| 536 | 540 | ||
| 537 | void RoomMember::Join(const std::string& nick, const std::string& console_id_hash, | 541 | void RoomMember::Join(const std::string& nick, const char* server_addr, u16 server_port, |
| 538 | const char* server_addr, u16 server_port, u16 client_port, | 542 | u16 client_port, const IPv4Address& preferred_fake_ip, |
| 539 | const MacAddress& preferred_mac, const std::string& password, | 543 | const std::string& password, const std::string& token) { |
| 540 | const std::string& token) { | ||
| 541 | // If the member is connected, kill the connection first | 544 | // If the member is connected, kill the connection first |
| 542 | if (room_member_impl->loop_thread && room_member_impl->loop_thread->joinable()) { | 545 | if (room_member_impl->loop_thread && room_member_impl->loop_thread->joinable()) { |
| 543 | Leave(); | 546 | Leave(); |
| @@ -571,7 +574,7 @@ void RoomMember::Join(const std::string& nick, const std::string& console_id_has | |||
| 571 | if (net > 0 && event.type == ENET_EVENT_TYPE_CONNECT) { | 574 | if (net > 0 && event.type == ENET_EVENT_TYPE_CONNECT) { |
| 572 | room_member_impl->nickname = nick; | 575 | room_member_impl->nickname = nick; |
| 573 | room_member_impl->StartLoop(); | 576 | room_member_impl->StartLoop(); |
| 574 | room_member_impl->SendJoinRequest(nick, console_id_hash, preferred_mac, password, token); | 577 | room_member_impl->SendJoinRequest(nick, preferred_fake_ip, password, token); |
| 575 | SendGameInfo(room_member_impl->current_game_info); | 578 | SendGameInfo(room_member_impl->current_game_info); |
| 576 | } else { | 579 | } else { |
| 577 | enet_peer_disconnect(room_member_impl->server, 0); | 580 | enet_peer_disconnect(room_member_impl->server, 0); |
| @@ -584,14 +587,22 @@ bool RoomMember::IsConnected() const { | |||
| 584 | return room_member_impl->IsConnected(); | 587 | return room_member_impl->IsConnected(); |
| 585 | } | 588 | } |
| 586 | 589 | ||
| 587 | void RoomMember::SendWifiPacket(const WifiPacket& wifi_packet) { | 590 | void RoomMember::SendProxyPacket(const ProxyPacket& proxy_packet) { |
| 588 | Packet packet; | 591 | Packet packet; |
| 589 | packet.Write(static_cast<u8>(IdWifiPacket)); | 592 | packet.Write(static_cast<u8>(IdProxyPacket)); |
| 590 | packet.Write(static_cast<u8>(wifi_packet.type)); | 593 | |
| 591 | packet.Write(wifi_packet.channel); | 594 | packet.Write(static_cast<u8>(proxy_packet.local_endpoint.family)); |
| 592 | packet.Write(wifi_packet.transmitter_address); | 595 | packet.Write(proxy_packet.local_endpoint.ip); |
| 593 | packet.Write(wifi_packet.destination_address); | 596 | packet.Write(proxy_packet.local_endpoint.portno); |
| 594 | packet.Write(wifi_packet.data); | 597 | |
| 598 | packet.Write(static_cast<u8>(proxy_packet.remote_endpoint.family)); | ||
| 599 | packet.Write(proxy_packet.remote_endpoint.ip); | ||
| 600 | packet.Write(proxy_packet.remote_endpoint.portno); | ||
| 601 | |||
| 602 | packet.Write(static_cast<u8>(proxy_packet.protocol)); | ||
| 603 | packet.Write(proxy_packet.broadcast); | ||
| 604 | packet.Write(proxy_packet.data); | ||
| 605 | |||
| 595 | room_member_impl->Send(std::move(packet)); | 606 | room_member_impl->Send(std::move(packet)); |
| 596 | } | 607 | } |
| 597 | 608 | ||
| @@ -645,8 +656,8 @@ RoomMember::CallbackHandle<RoomMember::Error> RoomMember::BindOnError( | |||
| 645 | return room_member_impl->Bind(callback); | 656 | return room_member_impl->Bind(callback); |
| 646 | } | 657 | } |
| 647 | 658 | ||
| 648 | RoomMember::CallbackHandle<WifiPacket> RoomMember::BindOnWifiPacketReceived( | 659 | RoomMember::CallbackHandle<ProxyPacket> RoomMember::BindOnProxyPacketReceived( |
| 649 | std::function<void(const WifiPacket&)> callback) { | 660 | std::function<void(const ProxyPacket&)> callback) { |
| 650 | return room_member_impl->Bind(callback); | 661 | return room_member_impl->Bind(callback); |
| 651 | } | 662 | } |
| 652 | 663 | ||
| @@ -685,7 +696,7 @@ void RoomMember::Leave() { | |||
| 685 | room_member_impl->client = nullptr; | 696 | room_member_impl->client = nullptr; |
| 686 | } | 697 | } |
| 687 | 698 | ||
| 688 | template void RoomMember::Unbind(CallbackHandle<WifiPacket>); | 699 | template void RoomMember::Unbind(CallbackHandle<ProxyPacket>); |
| 689 | template void RoomMember::Unbind(CallbackHandle<RoomMember::State>); | 700 | template void RoomMember::Unbind(CallbackHandle<RoomMember::State>); |
| 690 | template void RoomMember::Unbind(CallbackHandle<RoomMember::Error>); | 701 | template void RoomMember::Unbind(CallbackHandle<RoomMember::Error>); |
| 691 | template void RoomMember::Unbind(CallbackHandle<RoomInformation>); | 702 | template void RoomMember::Unbind(CallbackHandle<RoomInformation>); |
diff --git a/src/network/room_member.h b/src/network/room_member.h index bbb7d13d4..4252b7146 100644 --- a/src/network/room_member.h +++ b/src/network/room_member.h | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <vector> | 9 | #include <vector> |
| 10 | #include "common/announce_multiplayer_room.h" | 10 | #include "common/announce_multiplayer_room.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/socket_types.h" | ||
| 12 | #include "network/room.h" | 13 | #include "network/room.h" |
| 13 | 14 | ||
| 14 | namespace Network { | 15 | namespace Network { |
| @@ -17,22 +18,12 @@ using AnnounceMultiplayerRoom::GameInfo; | |||
| 17 | using AnnounceMultiplayerRoom::RoomInformation; | 18 | using AnnounceMultiplayerRoom::RoomInformation; |
| 18 | 19 | ||
| 19 | /// Information about the received WiFi packets. | 20 | /// Information about the received WiFi packets. |
| 20 | /// Acts as our own 802.11 header. | 21 | struct ProxyPacket { |
| 21 | struct WifiPacket { | 22 | SockAddrIn local_endpoint; |
| 22 | enum class PacketType : u8 { | 23 | SockAddrIn remote_endpoint; |
| 23 | Beacon, | 24 | Protocol protocol; |
| 24 | Data, | 25 | bool broadcast; |
| 25 | Authentication, | 26 | std::vector<u8> data; |
| 26 | AssociationResponse, | ||
| 27 | Deauthentication, | ||
| 28 | NodeMap | ||
| 29 | }; | ||
| 30 | PacketType type; ///< The type of 802.11 frame. | ||
| 31 | std::vector<u8> data; ///< Raw 802.11 frame data, starting at the management frame header | ||
| 32 | /// for management frames. | ||
| 33 | MacAddress transmitter_address; ///< Mac address of the transmitter. | ||
| 34 | MacAddress destination_address; ///< Mac address of the receiver. | ||
| 35 | u8 channel; ///< WiFi channel where this frame was transmitted. | ||
| 36 | }; | 27 | }; |
| 37 | 28 | ||
| 38 | /// Represents a chat message. | 29 | /// Represents a chat message. |
| @@ -72,15 +63,14 @@ public: | |||
| 72 | HostKicked, ///< Kicked by the host | 63 | HostKicked, ///< Kicked by the host |
| 73 | 64 | ||
| 74 | // Reasons why connection was rejected | 65 | // Reasons why connection was rejected |
| 75 | UnknownError, ///< Some error [permissions to network device missing or something] | 66 | UnknownError, ///< Some error [permissions to network device missing or something] |
| 76 | NameCollision, ///< Somebody is already using this name | 67 | NameCollision, ///< Somebody is already using this name |
| 77 | MacCollision, ///< Somebody is already using that mac-address | 68 | IpCollision, ///< Somebody is already using that fake-ip-address |
| 78 | ConsoleIdCollision, ///< Somebody in the room has the same Console ID | 69 | WrongVersion, ///< The room version is not the same as for this RoomMember |
| 79 | WrongVersion, ///< The room version is not the same as for this RoomMember | 70 | WrongPassword, ///< The password doesn't match the one from the Room |
| 80 | WrongPassword, ///< The password doesn't match the one from the Room | 71 | CouldNotConnect, ///< The room is not responding to a connection attempt |
| 81 | CouldNotConnect, ///< The room is not responding to a connection attempt | 72 | RoomIsFull, ///< Room is already at the maximum number of players |
| 82 | RoomIsFull, ///< Room is already at the maximum number of players | 73 | HostBanned, ///< The user is banned by the host |
| 83 | HostBanned, ///< The user is banned by the host | ||
| 84 | 74 | ||
| 85 | // Reasons why moderation request failed | 75 | // Reasons why moderation request failed |
| 86 | PermissionDenied, ///< The user does not have mod permissions | 76 | PermissionDenied, ///< The user does not have mod permissions |
| @@ -92,9 +82,9 @@ public: | |||
| 92 | std::string username; ///< The web services username of the member. Can be empty. | 82 | std::string username; ///< The web services username of the member. Can be empty. |
| 93 | std::string display_name; ///< The web services display name of the member. Can be empty. | 83 | std::string display_name; ///< The web services display name of the member. Can be empty. |
| 94 | std::string avatar_url; ///< Url to the member's avatar. Can be empty. | 84 | std::string avatar_url; ///< Url to the member's avatar. Can be empty. |
| 95 | GameInfo game_info; ///< Name of the game they're currently playing, or empty if they're | 85 | GameInfo game_info; ///< Name of the game they're currently playing, or empty if they're |
| 96 | /// not playing anything. | 86 | /// not playing anything. |
| 97 | MacAddress mac_address; ///< MAC address associated with this member. | 87 | IPv4Address fake_ip; ///< Fake Ip address associated with this member. |
| 98 | }; | 88 | }; |
| 99 | using MemberList = std::vector<MemberInformation>; | 89 | using MemberList = std::vector<MemberInformation>; |
| 100 | 90 | ||
| @@ -135,7 +125,7 @@ public: | |||
| 135 | /** | 125 | /** |
| 136 | * Returns the MAC address of the RoomMember. | 126 | * Returns the MAC address of the RoomMember. |
| 137 | */ | 127 | */ |
| 138 | const MacAddress& GetMacAddress() const; | 128 | const IPv4Address& GetFakeIpAddress() const; |
| 139 | 129 | ||
| 140 | /** | 130 | /** |
| 141 | * Returns information about the room we're currently connected to. | 131 | * Returns information about the room we're currently connected to. |
| @@ -149,19 +139,17 @@ public: | |||
| 149 | 139 | ||
| 150 | /** | 140 | /** |
| 151 | * Attempts to join a room at the specified address and port, using the specified nickname. | 141 | * Attempts to join a room at the specified address and port, using the specified nickname. |
| 152 | * A console ID hash is passed in to check console ID conflicts. | ||
| 153 | * This may fail if the username or console ID is already taken. | ||
| 154 | */ | 142 | */ |
| 155 | void Join(const std::string& nickname, const std::string& console_id_hash, | 143 | void Join(const std::string& nickname, const char* server_addr = "127.0.0.1", |
| 156 | const char* server_addr = "127.0.0.1", u16 server_port = DefaultRoomPort, | 144 | u16 server_port = DefaultRoomPort, u16 client_port = 0, |
| 157 | u16 client_port = 0, const MacAddress& preferred_mac = NoPreferredMac, | 145 | const IPv4Address& preferred_fake_ip = NoPreferredIP, |
| 158 | const std::string& password = "", const std::string& token = ""); | 146 | const std::string& password = "", const std::string& token = ""); |
| 159 | 147 | ||
| 160 | /** | 148 | /** |
| 161 | * Sends a WiFi packet to the room. | 149 | * Sends a WiFi packet to the room. |
| 162 | * @param packet The WiFi packet to send. | 150 | * @param packet The WiFi packet to send. |
| 163 | */ | 151 | */ |
| 164 | void SendWifiPacket(const WifiPacket& packet); | 152 | void SendProxyPacket(const ProxyPacket& packet); |
| 165 | 153 | ||
| 166 | /** | 154 | /** |
| 167 | * Sends a chat message to the room. | 155 | * Sends a chat message to the room. |
| @@ -207,14 +195,14 @@ public: | |||
| 207 | CallbackHandle<Error> BindOnError(std::function<void(const Error&)> callback); | 195 | CallbackHandle<Error> BindOnError(std::function<void(const Error&)> callback); |
| 208 | 196 | ||
| 209 | /** | 197 | /** |
| 210 | * Binds a function to an event that will be triggered every time a WifiPacket is received. | 198 | * Binds a function to an event that will be triggered every time a ProxyPacket is received. |
| 211 | * The function wil be called everytime the event is triggered. | 199 | * The function wil be called everytime the event is triggered. |
| 212 | * The callback function must not bind or unbind a function. Doing so will cause a deadlock | 200 | * The callback function must not bind or unbind a function. Doing so will cause a deadlock |
| 213 | * @param callback The function to call | 201 | * @param callback The function to call |
| 214 | * @return A handle used for removing the function from the registered list | 202 | * @return A handle used for removing the function from the registered list |
| 215 | */ | 203 | */ |
| 216 | CallbackHandle<WifiPacket> BindOnWifiPacketReceived( | 204 | CallbackHandle<ProxyPacket> BindOnProxyPacketReceived( |
| 217 | std::function<void(const WifiPacket&)> callback); | 205 | std::function<void(const ProxyPacket&)> callback); |
| 218 | 206 | ||
| 219 | /** | 207 | /** |
| 220 | * Binds a function to an event that will be triggered every time the RoomInformation changes. | 208 | * Binds a function to an event that will be triggered every time the RoomInformation changes. |
| @@ -292,10 +280,8 @@ inline const char* GetErrorStr(const RoomMember::Error& e) { | |||
| 292 | return "UnknownError"; | 280 | return "UnknownError"; |
| 293 | case RoomMember::Error::NameCollision: | 281 | case RoomMember::Error::NameCollision: |
| 294 | return "NameCollision"; | 282 | return "NameCollision"; |
| 295 | case RoomMember::Error::MacCollision: | 283 | case RoomMember::Error::IpCollision: |
| 296 | return "MaxCollision"; | 284 | return "IpCollision"; |
| 297 | case RoomMember::Error::ConsoleIdCollision: | ||
| 298 | return "ConsoleIdCollision"; | ||
| 299 | case RoomMember::Error::WrongVersion: | 285 | case RoomMember::Error::WrongVersion: |
| 300 | return "WrongVersion"; | 286 | return "WrongVersion"; |
| 301 | case RoomMember::Error::WrongPassword: | 287 | case RoomMember::Error::WrongPassword: |
diff --git a/src/web_service/verify_user_jwt.cpp b/src/web_service/verify_user_jwt.cpp index 3bff46f0a..129eb1968 100644 --- a/src/web_service/verify_user_jwt.cpp +++ b/src/web_service/verify_user_jwt.cpp | |||
| @@ -39,8 +39,10 @@ Network::VerifyUser::UserData VerifyUserJWT::LoadUserData(const std::string& ver | |||
| 39 | const std::string audience = fmt::format("external-{}", verify_uid); | 39 | const std::string audience = fmt::format("external-{}", verify_uid); |
| 40 | using namespace jwt::params; | 40 | using namespace jwt::params; |
| 41 | std::error_code error; | 41 | std::error_code error; |
| 42 | |||
| 43 | // We use the Citra backend so the issuer is citra-core | ||
| 42 | auto decoded = | 44 | auto decoded = |
| 43 | jwt::decode(token, algorithms({"rs256"}), error, secret(pub_key), issuer("yuzu-core"), | 45 | jwt::decode(token, algorithms({"rs256"}), error, secret(pub_key), issuer("citra-core"), |
| 44 | aud(audience), validate_iat(true), validate_jti(true)); | 46 | aud(audience), validate_iat(true), validate_jti(true)); |
| 45 | if (error) { | 47 | if (error) { |
| 46 | LOG_INFO(WebService, "Verification failed: category={}, code={}, message={}", | 48 | LOG_INFO(WebService, "Verification failed: category={}, code={}, message={}", |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 8bd1f92f7..e103df977 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -492,8 +492,6 @@ GMainWindow::~GMainWindow() { | |||
| 492 | delete render_window; | 492 | delete render_window; |
| 493 | } | 493 | } |
| 494 | 494 | ||
| 495 | system->GetRoomNetwork().Shutdown(); | ||
| 496 | |||
| 497 | #ifdef __linux__ | 495 | #ifdef __linux__ |
| 498 | ::close(sig_interrupt_fds[0]); | 496 | ::close(sig_interrupt_fds[0]); |
| 499 | ::close(sig_interrupt_fds[1]); | 497 | ::close(sig_interrupt_fds[1]); |
| @@ -3831,6 +3829,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) { | |||
| 3831 | 3829 | ||
| 3832 | render_window->close(); | 3830 | render_window->close(); |
| 3833 | multiplayer_state->Close(); | 3831 | multiplayer_state->Close(); |
| 3832 | system->GetRoomNetwork().Shutdown(); | ||
| 3834 | 3833 | ||
| 3835 | QWidget::closeEvent(event); | 3834 | QWidget::closeEvent(event); |
| 3836 | } | 3835 | } |
diff --git a/src/yuzu/multiplayer/chat_room.cpp b/src/yuzu/multiplayer/chat_room.cpp index 5837b36ab..1968a3c75 100644 --- a/src/yuzu/multiplayer/chat_room.cpp +++ b/src/yuzu/multiplayer/chat_room.cpp | |||
| @@ -316,21 +316,19 @@ void ChatRoom::OnStatusMessageReceive(const Network::StatusMessageEntry& status_ | |||
| 316 | } | 316 | } |
| 317 | 317 | ||
| 318 | void ChatRoom::OnSendChat() { | 318 | void ChatRoom::OnSendChat() { |
| 319 | if (auto room = room_network->GetRoomMember().lock()) { | 319 | if (auto room_member = room_network->GetRoomMember().lock()) { |
| 320 | if (room->GetState() != Network::RoomMember::State::Joined && | 320 | if (!room_member->IsConnected()) { |
| 321 | room->GetState() != Network::RoomMember::State::Moderator) { | ||
| 322 | |||
| 323 | return; | 321 | return; |
| 324 | } | 322 | } |
| 325 | auto message = ui->chat_message->text().toStdString(); | 323 | auto message = ui->chat_message->text().toStdString(); |
| 326 | if (!ValidateMessage(message)) { | 324 | if (!ValidateMessage(message)) { |
| 327 | return; | 325 | return; |
| 328 | } | 326 | } |
| 329 | auto nick = room->GetNickname(); | 327 | auto nick = room_member->GetNickname(); |
| 330 | auto username = room->GetUsername(); | 328 | auto username = room_member->GetUsername(); |
| 331 | Network::ChatEntry chat{nick, username, message}; | 329 | Network::ChatEntry chat{nick, username, message}; |
| 332 | 330 | ||
| 333 | auto members = room->GetMemberInformation(); | 331 | auto members = room_member->GetMemberInformation(); |
| 334 | auto it = std::find_if(members.begin(), members.end(), | 332 | auto it = std::find_if(members.begin(), members.end(), |
| 335 | [&chat](const Network::RoomMember::MemberInformation& member) { | 333 | [&chat](const Network::RoomMember::MemberInformation& member) { |
| 336 | return member.nickname == chat.nickname && | 334 | return member.nickname == chat.nickname && |
| @@ -341,7 +339,7 @@ void ChatRoom::OnSendChat() { | |||
| 341 | } | 339 | } |
| 342 | auto player = std::distance(members.begin(), it); | 340 | auto player = std::distance(members.begin(), it); |
| 343 | ChatMessage m(chat, *room_network); | 341 | ChatMessage m(chat, *room_network); |
| 344 | room->SendChatMessage(message); | 342 | room_member->SendChatMessage(message); |
| 345 | AppendChatMessage(m.GetPlayerChatMessage(player)); | 343 | AppendChatMessage(m.GetPlayerChatMessage(player)); |
| 346 | ui->chat_message->clear(); | 344 | ui->chat_message->clear(); |
| 347 | } | 345 | } |
diff --git a/src/yuzu/multiplayer/client_room.cpp b/src/yuzu/multiplayer/client_room.cpp index a9859ed70..86baafbf0 100644 --- a/src/yuzu/multiplayer/client_room.cpp +++ b/src/yuzu/multiplayer/client_room.cpp | |||
| @@ -74,7 +74,6 @@ void ClientRoomWindow::OnRoomUpdate(const Network::RoomInformation& info) { | |||
| 74 | void ClientRoomWindow::OnStateChange(const Network::RoomMember::State& state) { | 74 | void ClientRoomWindow::OnStateChange(const Network::RoomMember::State& state) { |
| 75 | if (state == Network::RoomMember::State::Joined || | 75 | if (state == Network::RoomMember::State::Joined || |
| 76 | state == Network::RoomMember::State::Moderator) { | 76 | state == Network::RoomMember::State::Moderator) { |
| 77 | |||
| 78 | ui->chat->Clear(); | 77 | ui->chat->Clear(); |
| 79 | ui->chat->AppendStatusMessage(tr("Connected")); | 78 | ui->chat->AppendStatusMessage(tr("Connected")); |
| 80 | SetModPerms(state == Network::RoomMember::State::Moderator); | 79 | SetModPerms(state == Network::RoomMember::State::Moderator); |
diff --git a/src/yuzu/multiplayer/direct_connect.cpp b/src/yuzu/multiplayer/direct_connect.cpp index 9000c4531..4c0ea0a6b 100644 --- a/src/yuzu/multiplayer/direct_connect.cpp +++ b/src/yuzu/multiplayer/direct_connect.cpp | |||
| @@ -97,9 +97,9 @@ void DirectConnectWindow::Connect() { | |||
| 97 | QFuture<void> f = QtConcurrent::run([&] { | 97 | QFuture<void> f = QtConcurrent::run([&] { |
| 98 | if (auto room_member = room_network.GetRoomMember().lock()) { | 98 | if (auto room_member = room_network.GetRoomMember().lock()) { |
| 99 | auto port = UISettings::values.multiplayer_port.GetValue(); | 99 | auto port = UISettings::values.multiplayer_port.GetValue(); |
| 100 | room_member->Join(ui->nickname->text().toStdString(), "", | 100 | room_member->Join(ui->nickname->text().toStdString(), |
| 101 | ui->ip->text().toStdString().c_str(), port, 0, | 101 | ui->ip->text().toStdString().c_str(), port, 0, Network::NoPreferredIP, |
| 102 | Network::NoPreferredMac, ui->password->text().toStdString().c_str()); | 102 | ui->password->text().toStdString().c_str()); |
| 103 | } | 103 | } |
| 104 | }); | 104 | }); |
| 105 | watcher->setFuture(f); | 105 | watcher->setFuture(f); |
| @@ -121,9 +121,7 @@ void DirectConnectWindow::OnConnection() { | |||
| 121 | EndConnecting(); | 121 | EndConnecting(); |
| 122 | 122 | ||
| 123 | if (auto room_member = room_network.GetRoomMember().lock()) { | 123 | if (auto room_member = room_network.GetRoomMember().lock()) { |
| 124 | if (room_member->GetState() == Network::RoomMember::State::Joined || | 124 | if (room_member->IsConnected()) { |
| 125 | room_member->GetState() == Network::RoomMember::State::Moderator) { | ||
| 126 | |||
| 127 | close(); | 125 | close(); |
| 128 | } | 126 | } |
| 129 | } | 127 | } |
diff --git a/src/yuzu/multiplayer/host_room.cpp b/src/yuzu/multiplayer/host_room.cpp index cb9464b2b..d70a9a3c8 100644 --- a/src/yuzu/multiplayer/host_room.cpp +++ b/src/yuzu/multiplayer/host_room.cpp | |||
| @@ -201,8 +201,8 @@ void HostRoomWindow::Host() { | |||
| 201 | } | 201 | } |
| 202 | #endif | 202 | #endif |
| 203 | // TODO: Check what to do with this | 203 | // TODO: Check what to do with this |
| 204 | member->Join(ui->username->text().toStdString(), "", "127.0.0.1", port, 0, | 204 | member->Join(ui->username->text().toStdString(), "127.0.0.1", port, 0, |
| 205 | Network::NoPreferredMac, password, token); | 205 | Network::NoPreferredIP, password, token); |
| 206 | 206 | ||
| 207 | // Store settings | 207 | // Store settings |
| 208 | UISettings::values.multiplayer_room_nickname = ui->username->text(); | 208 | UISettings::values.multiplayer_room_nickname = ui->username->text(); |
diff --git a/src/yuzu/multiplayer/lobby.cpp b/src/yuzu/multiplayer/lobby.cpp index 23c2f21ab..1cc518279 100644 --- a/src/yuzu/multiplayer/lobby.cpp +++ b/src/yuzu/multiplayer/lobby.cpp | |||
| @@ -169,7 +169,7 @@ void Lobby::OnJoinRoom(const QModelIndex& source) { | |||
| 169 | } | 169 | } |
| 170 | #endif | 170 | #endif |
| 171 | if (auto room_member = room_network.GetRoomMember().lock()) { | 171 | if (auto room_member = room_network.GetRoomMember().lock()) { |
| 172 | room_member->Join(nickname, "", ip.c_str(), port, 0, Network::NoPreferredMac, password, | 172 | room_member->Join(nickname, ip.c_str(), port, 0, Network::NoPreferredIP, password, |
| 173 | token); | 173 | token); |
| 174 | } | 174 | } |
| 175 | }); | 175 | }); |
diff --git a/src/yuzu/multiplayer/message.cpp b/src/yuzu/multiplayer/message.cpp index 76ec276ad..94d7a38b8 100644 --- a/src/yuzu/multiplayer/message.cpp +++ b/src/yuzu/multiplayer/message.cpp | |||
| @@ -43,11 +43,8 @@ const ConnectionError ErrorManager::LOST_CONNECTION( | |||
| 43 | QT_TR_NOOP("Connection to room lost. Try to reconnect.")); | 43 | QT_TR_NOOP("Connection to room lost. Try to reconnect.")); |
| 44 | const ConnectionError ErrorManager::HOST_KICKED( | 44 | const ConnectionError ErrorManager::HOST_KICKED( |
| 45 | QT_TR_NOOP("You have been kicked by the room host.")); | 45 | QT_TR_NOOP("You have been kicked by the room host.")); |
| 46 | const ConnectionError ErrorManager::MAC_COLLISION( | 46 | const ConnectionError ErrorManager::IP_COLLISION( |
| 47 | QT_TR_NOOP("MAC address is already in use. Please choose another.")); | 47 | QT_TR_NOOP("IP address is already in use. Please choose another.")); |
| 48 | const ConnectionError ErrorManager::CONSOLE_ID_COLLISION(QT_TR_NOOP( | ||
| 49 | "Your Console ID conflicted with someone else's in the room.\n\nPlease go to Emulation " | ||
| 50 | "> Configure > System to regenerate your Console ID.")); | ||
| 51 | const ConnectionError ErrorManager::PERMISSION_DENIED( | 48 | const ConnectionError ErrorManager::PERMISSION_DENIED( |
| 52 | 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.")); |
| 53 | const ConnectionError ErrorManager::NO_SUCH_USER(QT_TR_NOOP( | 50 | const ConnectionError ErrorManager::NO_SUCH_USER(QT_TR_NOOP( |
diff --git a/src/yuzu/multiplayer/message.h b/src/yuzu/multiplayer/message.h index eb5c8d1be..812495c72 100644 --- a/src/yuzu/multiplayer/message.h +++ b/src/yuzu/multiplayer/message.h | |||
| @@ -40,8 +40,7 @@ public: | |||
| 40 | static const ConnectionError GENERIC_ERROR; | 40 | static const ConnectionError GENERIC_ERROR; |
| 41 | static const ConnectionError LOST_CONNECTION; | 41 | static const ConnectionError LOST_CONNECTION; |
| 42 | static const ConnectionError HOST_KICKED; | 42 | static const ConnectionError HOST_KICKED; |
| 43 | static const ConnectionError MAC_COLLISION; | 43 | static const ConnectionError IP_COLLISION; |
| 44 | static const ConnectionError CONSOLE_ID_COLLISION; | ||
| 45 | static const ConnectionError PERMISSION_DENIED; | 44 | static const ConnectionError PERMISSION_DENIED; |
| 46 | static const ConnectionError NO_SUCH_USER; | 45 | static const ConnectionError NO_SUCH_USER; |
| 47 | /** | 46 | /** |
diff --git a/src/yuzu/multiplayer/state.cpp b/src/yuzu/multiplayer/state.cpp index 4149b5232..dba76b22b 100644 --- a/src/yuzu/multiplayer/state.cpp +++ b/src/yuzu/multiplayer/state.cpp | |||
| @@ -59,7 +59,9 @@ MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_lis | |||
| 59 | }); | 59 | }); |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | MultiplayerState::~MultiplayerState() { | 62 | MultiplayerState::~MultiplayerState() = default; |
| 63 | |||
| 64 | void MultiplayerState::Close() { | ||
| 63 | if (state_callback_handle) { | 65 | if (state_callback_handle) { |
| 64 | if (auto member = room_network.GetRoomMember().lock()) { | 66 | if (auto member = room_network.GetRoomMember().lock()) { |
| 65 | member->Unbind(state_callback_handle); | 67 | member->Unbind(state_callback_handle); |
| @@ -71,9 +73,6 @@ MultiplayerState::~MultiplayerState() { | |||
| 71 | member->Unbind(error_callback_handle); | 73 | member->Unbind(error_callback_handle); |
| 72 | } | 74 | } |
| 73 | } | 75 | } |
| 74 | } | ||
| 75 | |||
| 76 | void MultiplayerState::Close() { | ||
| 77 | if (host_room) { | 76 | if (host_room) { |
| 78 | host_room->close(); | 77 | host_room->close(); |
| 79 | } | 78 | } |
| @@ -95,7 +94,6 @@ void MultiplayerState::retranslateUi() { | |||
| 95 | status_text->setText(tr("Not Connected. Click here to find a room!")); | 94 | status_text->setText(tr("Not Connected. Click here to find a room!")); |
| 96 | } else if (current_state == Network::RoomMember::State::Joined || | 95 | } else if (current_state == Network::RoomMember::State::Joined || |
| 97 | current_state == Network::RoomMember::State::Moderator) { | 96 | current_state == Network::RoomMember::State::Moderator) { |
| 98 | |||
| 99 | status_text->setText(tr("Connected")); | 97 | status_text->setText(tr("Connected")); |
| 100 | } else { | 98 | } else { |
| 101 | status_text->setText(tr("Not Connected")); | 99 | status_text->setText(tr("Not Connected")); |
| @@ -151,11 +149,8 @@ void MultiplayerState::OnNetworkError(const Network::RoomMember::Error& error) { | |||
| 151 | NetworkMessage::ErrorManager::ShowError( | 149 | NetworkMessage::ErrorManager::ShowError( |
| 152 | NetworkMessage::ErrorManager::USERNAME_NOT_VALID_SERVER); | 150 | NetworkMessage::ErrorManager::USERNAME_NOT_VALID_SERVER); |
| 153 | break; | 151 | break; |
| 154 | case Network::RoomMember::Error::MacCollision: | 152 | case Network::RoomMember::Error::IpCollision: |
| 155 | NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::MAC_COLLISION); | 153 | NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::IP_COLLISION); |
| 156 | break; | ||
| 157 | case Network::RoomMember::Error::ConsoleIdCollision: | ||
| 158 | NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::CONSOLE_ID_COLLISION); | ||
| 159 | break; | 154 | break; |
| 160 | case Network::RoomMember::Error::RoomIsFull: | 155 | case Network::RoomMember::Error::RoomIsFull: |
| 161 | NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::ROOM_IS_FULL); | 156 | NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::ROOM_IS_FULL); |
diff --git a/src/yuzu/multiplayer/validation.h b/src/yuzu/multiplayer/validation.h index 7d48e589d..dabf860be 100644 --- a/src/yuzu/multiplayer/validation.h +++ b/src/yuzu/multiplayer/validation.h | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | class Validation { | 10 | class Validation { |
| 11 | public: | 11 | public: |
| 12 | Validation() | 12 | Validation() |
| 13 | : room_name(room_name_regex), nickname(nickname_regex), ip(ip_regex), port(0, 65535) {} | 13 | : room_name(room_name_regex), nickname(nickname_regex), ip(ip_regex), port(0, UINT16_MAX) {} |
| 14 | 14 | ||
| 15 | ~Validation() = default; | 15 | ~Validation() = default; |
| 16 | 16 | ||
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 25d1bf1e6..e12d414d9 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h | |||
| @@ -104,11 +104,12 @@ struct Values { | |||
| 104 | // multiplayer settings | 104 | // multiplayer settings |
| 105 | Settings::Setting<QString> multiplayer_nickname{QStringLiteral("yuzu"), "nickname"}; | 105 | Settings::Setting<QString> multiplayer_nickname{QStringLiteral("yuzu"), "nickname"}; |
| 106 | Settings::Setting<QString> multiplayer_ip{{}, "ip"}; | 106 | Settings::Setting<QString> multiplayer_ip{{}, "ip"}; |
| 107 | Settings::SwitchableSetting<uint, true> multiplayer_port{24872, 0, 65535, "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"}; |
| 109 | Settings::Setting<QString> multiplayer_room_name{{}, "room_name"}; | 109 | Settings::Setting<QString> multiplayer_room_name{{}, "room_name"}; |
| 110 | Settings::SwitchableSetting<uint, true> multiplayer_max_player{8, 0, 8, "max_player"}; | 110 | Settings::SwitchableSetting<uint, true> multiplayer_max_player{8, 0, 8, "max_player"}; |
| 111 | Settings::SwitchableSetting<uint, true> multiplayer_room_port{24872, 0, 65535, "room_port"}; | 111 | Settings::SwitchableSetting<uint, true> multiplayer_room_port{24872, 0, UINT16_MAX, |
| 112 | "room_port"}; | ||
| 112 | Settings::SwitchableSetting<uint, true> multiplayer_host_type{0, 0, 1, "host_type"}; | 113 | Settings::SwitchableSetting<uint, true> multiplayer_host_type{0, 0, 1, "host_type"}; |
| 113 | Settings::Setting<qulonglong> multiplayer_game_id{{}, "game_id"}; | 114 | Settings::Setting<qulonglong> multiplayer_game_id{{}, "game_id"}; |
| 114 | Settings::Setting<QString> multiplayer_room_description{{}, "room_description"}; | 115 | Settings::Setting<QString> multiplayer_room_description{{}, "room_description"}; |
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 003890c07..3a0f33cba 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -108,15 +108,11 @@ static void OnNetworkError(const Network::RoomMember::Error& error) { | |||
| 108 | "You tried to use the same nickname as another user that is connected to the Room"); | 108 | "You tried to use the same nickname as another user that is connected to the Room"); |
| 109 | exit(1); | 109 | exit(1); |
| 110 | break; | 110 | break; |
| 111 | case Network::RoomMember::Error::MacCollision: | 111 | case Network::RoomMember::Error::IpCollision: |
| 112 | LOG_ERROR(Network, "You tried to use the same MAC-Address as another user that is " | 112 | LOG_ERROR(Network, "You tried to use the same fake IP-Address as another user that is " |
| 113 | "connected to the Room"); | 113 | "connected to the Room"); |
| 114 | exit(1); | 114 | exit(1); |
| 115 | break; | 115 | break; |
| 116 | case Network::RoomMember::Error::ConsoleIdCollision: | ||
| 117 | LOG_ERROR(Network, "Your Console ID conflicted with someone else in the Room"); | ||
| 118 | exit(1); | ||
| 119 | break; | ||
| 120 | case Network::RoomMember::Error::WrongPassword: | 116 | case Network::RoomMember::Error::WrongPassword: |
| 121 | LOG_ERROR(Network, "Room replied with: Wrong password"); | 117 | LOG_ERROR(Network, "Room replied with: Wrong password"); |
| 122 | exit(1); | 118 | exit(1); |
| @@ -365,7 +361,7 @@ int main(int argc, char** argv) { | |||
| 365 | member->BindOnError(OnNetworkError); | 361 | member->BindOnError(OnNetworkError); |
| 366 | LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", address, port, | 362 | LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", address, port, |
| 367 | nickname); | 363 | nickname); |
| 368 | member->Join(nickname, "", address.c_str(), port, 0, Network::NoPreferredMac, password); | 364 | member->Join(nickname, address.c_str(), port, 0, Network::NoPreferredIP, password); |
| 369 | } else { | 365 | } else { |
| 370 | LOG_ERROR(Network, "Could not access RoomMember"); | 366 | LOG_ERROR(Network, "Could not access RoomMember"); |
| 371 | return 0; | 367 | return 0; |