diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/citra/default_ini.h | 6 | ||||
-rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/core/core.cpp | 5 | ||||
-rw-r--r-- | src/core/hle/service/apt/apt.cpp | 229 | ||||
-rw-r--r-- | src/core/hle/service/apt/apt.h | 6 | ||||
-rw-r--r-- | src/core/hle/service/dsp_dsp.cpp | 7 | ||||
-rw-r--r-- | src/core/loader/ncch.cpp | 8 | ||||
-rw-r--r-- | src/input_common/main.h | 2 | ||||
-rw-r--r-- | src/input_common/sdl/sdl.cpp | 2 | ||||
-rw-r--r-- | src/network/packet.cpp | 38 | ||||
-rw-r--r-- | src/network/packet.h | 4 | ||||
-rw-r--r-- | src/network/room.cpp | 84 | ||||
-rw-r--r-- | src/network/room.h | 19 | ||||
-rw-r--r-- | src/network/room_member.cpp | 128 | ||||
-rw-r--r-- | src/network/room_member.h | 59 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_shader_gen.cpp | 9 |
16 files changed, 524 insertions, 84 deletions
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index a12498e0f..b0a0ebd3b 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h @@ -12,7 +12,7 @@ const char* sdl2_config_file = R"( # It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..." # Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values -# for button input, the following devices are avaible: +# for button input, the following devices are available: # - "keyboard" (default) for keyboard input. Required parameters: # - "code": the code of the key to bind # - "sdl" for joystick input using SDL. Required parameters: @@ -21,7 +21,7 @@ const char* sdl2_config_file = R"( # - "hat"(optional): the index of the hat to bind as direction buttons # - "axis"(optional): the index of the axis to bind # - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right" -# - "threshould"(only used for axis): a float value in (-1.0, 1.0) which the button is +# - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is # triggered if the axis value crosses # - "direction"(only used for axis): "+" means the button is triggered when the axis value # is greater than the threshold; "-" means the button is triggered when the axis value @@ -42,7 +42,7 @@ button_zl= button_zr= button_home= -# for analog input, the following devices are avaible: +# for analog input, the following devices are available: # - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters: # - "up", "down", "left", "right": sub-devices for each direction. # Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00" diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 360f407f3..0a6f97e4b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -388,7 +388,7 @@ set(HEADERS create_directory_groups(${SRCS} ${HEADERS}) add_library(core STATIC ${SRCS} ${HEADERS}) -target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) +target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core) target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt) if (ENABLE_WEB_SERVICE) target_link_libraries(core PUBLIC json-headers web_service) diff --git a/src/core/core.cpp b/src/core/core.cpp index d08f18623..5332318cf 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -19,6 +19,7 @@ #include "core/loader/loader.h" #include "core/memory_setup.h" #include "core/settings.h" +#include "network/network.h" #include "video_core/video_core.h" namespace Core { @@ -188,6 +189,10 @@ void System::Shutdown() { cpu_core = nullptr; app_loader = nullptr; telemetry_session = nullptr; + if (auto room_member = Network::GetRoomMember().lock()) { + Network::GameInfo game_info{}; + room_member->SendGameInfo(game_info); + } LOG_DEBUG(Core, "Shutdown OK"); } diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 0109fa2b2..58d94768c 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -34,8 +34,6 @@ static bool shared_font_loaded = false; static bool shared_font_relocated = false; static Kernel::SharedPtr<Kernel::Mutex> lock; -static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event -static Kernel::SharedPtr<Kernel::Event> parameter_event; ///< APT parameter event static u32 cpu_percent; ///< CPU time available to the running application @@ -44,32 +42,160 @@ static u8 unknown_ns_state_field; static ScreencapPostPermission screen_capture_post_permission; -/// Parameter data to be returned in the next call to Glance/ReceiveParameter +/// Parameter data to be returned in the next call to Glance/ReceiveParameter. +/// TODO(Subv): Use std::optional once we migrate to C++17. static boost::optional<MessageParameter> next_parameter; +enum class AppletPos { Application = 0, Library = 1, System = 2, SysLibrary = 3, Resident = 4 }; + +static constexpr size_t NumAppletSlot = 4; + +enum class AppletSlot : u8 { + Application, + SystemApplet, + HomeMenu, + LibraryApplet, + + // An invalid tag + Error, +}; + +union AppletAttributes { + u32 raw; + + BitField<0, 3, u32> applet_pos; + + AppletAttributes() : raw(0) {} + AppletAttributes(u32 attributes) : raw(attributes) {} +}; + +struct AppletSlotData { + AppletId applet_id; + AppletSlot slot; + bool registered; + AppletAttributes attributes; + Kernel::SharedPtr<Kernel::Event> notification_event; + Kernel::SharedPtr<Kernel::Event> parameter_event; +}; + +// Holds data about the concurrently running applets in the system. +static std::array<AppletSlotData, NumAppletSlot> applet_slots = {}; + +// This overload returns nullptr if no applet with the specified id has been started. +static AppletSlotData* GetAppletSlotData(AppletId id) { + auto GetSlot = [](AppletSlot slot) -> AppletSlotData* { + return &applet_slots[static_cast<size_t>(slot)]; + }; + + if (id == AppletId::Application) { + auto* slot = GetSlot(AppletSlot::Application); + if (slot->applet_id != AppletId::None) + return slot; + + return nullptr; + } + + if (id == AppletId::AnySystemApplet) { + auto* system_slot = GetSlot(AppletSlot::SystemApplet); + if (system_slot->applet_id != AppletId::None) + return system_slot; + + // The Home Menu is also a system applet, but it lives in its own slot to be able to run + // concurrently with other system applets. + auto* home_slot = GetSlot(AppletSlot::HomeMenu); + if (home_slot->applet_id != AppletId::None) + return home_slot; + + return nullptr; + } + + if (id == AppletId::AnyLibraryApplet || id == AppletId::AnySysLibraryApplet) { + auto* slot = GetSlot(AppletSlot::LibraryApplet); + if (slot->applet_id == AppletId::None) + return nullptr; + + u32 applet_pos = slot->attributes.applet_pos; + + if (id == AppletId::AnyLibraryApplet && applet_pos == static_cast<u32>(AppletPos::Library)) + return slot; + + if (id == AppletId::AnySysLibraryApplet && + applet_pos == static_cast<u32>(AppletPos::SysLibrary)) + return slot; + + return nullptr; + } + + if (id == AppletId::HomeMenu || id == AppletId::AlternateMenu) { + auto* slot = GetSlot(AppletSlot::HomeMenu); + if (slot->applet_id != AppletId::None) + return slot; + + return nullptr; + } + + for (auto& slot : applet_slots) { + if (slot.applet_id == id) + return &slot; + } + + return nullptr; +} + +static AppletSlotData* GetAppletSlotData(AppletAttributes attributes) { + // Mapping from AppletPos to AppletSlot + static constexpr std::array<AppletSlot, 6> applet_position_slots = { + AppletSlot::Application, AppletSlot::LibraryApplet, AppletSlot::SystemApplet, + AppletSlot::LibraryApplet, AppletSlot::Error, AppletSlot::LibraryApplet}; + + u32 applet_pos = attributes.applet_pos; + if (applet_pos >= applet_position_slots.size()) + return nullptr; + + AppletSlot slot = applet_position_slots[applet_pos]; + + if (slot == AppletSlot::Error) + return nullptr; + + return &applet_slots[static_cast<size_t>(slot)]; +} + void SendParameter(const MessageParameter& parameter) { next_parameter = parameter; - // Signal the event to let the application know that a new parameter is ready to be read - parameter_event->Signal(); + // Signal the event to let the receiver know that a new parameter is ready to be read + auto* const slot_data = GetAppletSlotData(static_cast<AppletId>(parameter.destination_id)); + ASSERT(slot_data); + + slot_data->parameter_event->Signal(); } void Initialize(Service::Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2, 2, 0); // 0x20080 u32 app_id = rp.Pop<u32>(); - u32 flags = rp.Pop<u32>(); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 3); - rb.Push(RESULT_SUCCESS); - rb.PushCopyHandles(Kernel::g_handle_table.Create(notification_event).Unwrap(), - Kernel::g_handle_table.Create(parameter_event).Unwrap()); + u32 attributes = rp.Pop<u32>(); - // TODO(bunnei): Check if these events are cleared every time Initialize is called. - notification_event->Clear(); - parameter_event->Clear(); + LOG_DEBUG(Service_APT, "called app_id=0x%08X, attributes=0x%08X", app_id, attributes); + + auto* const slot_data = GetAppletSlotData(attributes); + + // Note: The real NS service does not check if the attributes value is valid before accessing + // the data in the array + ASSERT_MSG(slot_data, "Invalid application attributes"); + + if (slot_data->registered) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; + } - ASSERT_MSG((nullptr != lock), "Cannot initialize without lock"); - lock->Release(); + slot_data->applet_id = static_cast<AppletId>(app_id); + slot_data->attributes.raw = attributes; - LOG_DEBUG(Service_APT, "called app_id=0x%08X, flags=0x%08X", app_id, flags); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 3); + rb.Push(RESULT_SUCCESS); + rb.PushCopyHandles(Kernel::g_handle_table.Create(slot_data->notification_event).Unwrap(), + Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap()); } void GetSharedFont(Service::Interface* self) { @@ -120,7 +246,12 @@ void GetLockHandle(Service::Interface* self) { // this will cause the app to wait until parameter_event is signaled. u32 applet_attributes = rp.Pop<u32>(); IPC::RequestBuilder rb = rp.MakeBuilder(3, 2); - rb.Push(RESULT_SUCCESS); // No error + rb.Push(RESULT_SUCCESS); // No error + + // TODO(Subv): The output attributes should have an AppletPos of either Library or System | + // Library (depending on the type of the last launched applet) if the input attributes' + // AppletPos has the Library bit set. + rb.Push(applet_attributes); // Applet Attributes, this value is passed to Enable. rb.Push<u32>(0); // Least significant bit = power button state Kernel::Handle handle_copy = Kernel::g_handle_table.Create(lock).Unwrap(); @@ -133,10 +264,22 @@ void GetLockHandle(Service::Interface* self) { void Enable(Service::Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3, 1, 0); // 0x30040 u32 attributes = rp.Pop<u32>(); + + LOG_DEBUG(Service_APT, "called attributes=0x%08X", attributes); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(RESULT_SUCCESS); // No error - parameter_event->Signal(); // Let the application know that it has been started - LOG_WARNING(Service_APT, "(STUBBED) called attributes=0x%08X", attributes); + + auto* const slot_data = GetAppletSlotData(attributes); + + if (!slot_data) { + rb.Push(ResultCode(ErrCodes::InvalidAppletSlot, ErrorModule::Applet, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; + } + + slot_data->registered = true; + + rb.Push(RESULT_SUCCESS); } void GetAppletManInfo(Service::Interface* self) { @@ -154,22 +297,27 @@ void GetAppletManInfo(Service::Interface* self) { void IsRegistered(Service::Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x9, 1, 0); // 0x90040 - u32 app_id = rp.Pop<u32>(); + AppletId app_id = static_cast<AppletId>(rp.Pop<u32>()); IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); // No error - // TODO(Subv): An application is considered "registered" if it has already called APT::Enable - // handle this properly once we implement multiprocess support. - bool is_registered = false; // Set to not registered by default + auto* const slot_data = GetAppletSlotData(app_id); + + // Check if an LLE applet was registered first, then fallback to HLE applets + bool is_registered = slot_data && slot_data->registered; - if (app_id == static_cast<u32>(AppletId::AnyLibraryApplet)) { - is_registered = HLE::Applets::IsLibraryAppletRunning(); - } else if (auto applet = HLE::Applets::Applet::Get(static_cast<AppletId>(app_id))) { - is_registered = true; // Set to registered + if (!is_registered) { + if (app_id == AppletId::AnyLibraryApplet) { + is_registered = HLE::Applets::IsLibraryAppletRunning(); + } else if (auto applet = HLE::Applets::Applet::Get(app_id)) { + // The applet exists, set it as registered. + is_registered = true; + } } + rb.Push(is_registered); - LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id); + LOG_DEBUG(Service_APT, "called app_id=0x%08X", static_cast<u32>(app_id)); } void InquireNotification(Service::Interface* self) { @@ -864,14 +1012,23 @@ void Init() { screen_capture_post_permission = ScreencapPostPermission::CleanThePermission; // TODO(JamePeng): verify the initial value - // TODO(bunnei): Check if these are created in Initialize or on APT process startup. - notification_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Notification"); - parameter_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Start"); + for (size_t slot = 0; slot < applet_slots.size(); ++slot) { + auto& slot_data = applet_slots[slot]; + slot_data.slot = static_cast<AppletSlot>(slot); + slot_data.applet_id = AppletId::None; + slot_data.attributes.raw = 0; + slot_data.registered = false; + slot_data.notification_event = + Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Notification"); + slot_data.parameter_event = + Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Parameter"); + } // Initialize the parameter to wake up the application. next_parameter.emplace(); next_parameter->signal = static_cast<u32>(SignalType::Wakeup); next_parameter->destination_id = static_cast<u32>(AppletId::Application); + applet_slots[static_cast<size_t>(AppletSlot::Application)].parameter_event->Signal(); } void Shutdown() { @@ -879,8 +1036,12 @@ void Shutdown() { shared_font_loaded = false; shared_font_relocated = false; lock = nullptr; - notification_event = nullptr; - parameter_event = nullptr; + + for (auto& slot : applet_slots) { + slot.registered = false; + slot.notification_event = nullptr; + slot.parameter_event = nullptr; + } next_parameter = boost::none; diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index 106754853..96b28b438 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -72,6 +72,8 @@ enum class SignalType : u32 { /// App Id's used by APT functions enum class AppletId : u32 { + None = 0, + AnySystemApplet = 0x100, HomeMenu = 0x101, AlternateMenu = 0x103, Camera = 0x110, @@ -83,6 +85,7 @@ enum class AppletId : u32 { Miiverse = 0x117, MiiversePost = 0x118, AmiiboSettings = 0x119, + AnySysLibraryApplet = 0x200, SoftwareKeyboard1 = 0x201, Ed1 = 0x202, PnoteApp = 0x204, @@ -119,8 +122,9 @@ enum class ScreencapPostPermission : u32 { namespace ErrCodes { enum { ParameterPresent = 2, + InvalidAppletSlot = 4, }; -} +} // namespace ErrCodes /// Send a parameter to the currently-running application, which will read it via ReceiveParameter void SendParameter(const MessageParameter& parameter); diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index 7d746054f..42f8950f9 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp @@ -147,9 +147,10 @@ static void LoadComponent(Service::Interface* self) { LOG_INFO(Service_DSP, "Firmware hash: %#" PRIx64, Common::ComputeHash64(component_data.data(), component_data.size())); // Some versions of the firmware have the location of DSP structures listed here. - ASSERT(size > 0x37C); - LOG_INFO(Service_DSP, "Structures hash: %#" PRIx64, - Common::ComputeHash64(component_data.data() + 0x340, 60)); + if (size > 0x37C) { + LOG_INFO(Service_DSP, "Structures hash: %#" PRIx64, + Common::ComputeHash64(component_data.data() + 0x340, 60)); + } LOG_WARNING(Service_DSP, "(STUBBED) called size=0x%X, prog_mask=0x%08X, data_mask=0x%08X, buffer=0x%08X", diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index c007069a9..7aff7f29b 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -20,6 +20,7 @@ #include "core/loader/ncch.h" #include "core/loader/smdh.h" #include "core/memory.h" +#include "network/network.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // Loader namespace @@ -350,6 +351,13 @@ ResultStatus AppLoader_NCCH::Load() { Core::Telemetry().AddField(Telemetry::FieldType::Session, "ProgramId", program_id); + if (auto room_member = Network::GetRoomMember().lock()) { + Network::GameInfo game_info; + ReadTitle(game_info.name); + game_info.id = ncch_header.program_id; + room_member->SendGameInfo(game_info); + } + is_loaded = true; // Set state to loaded result = LoadExec(); // Load the executable into memory for booting diff --git a/src/input_common/main.h b/src/input_common/main.h index 140bbd014..e7fb3890e 100644 --- a/src/input_common/main.h +++ b/src/input_common/main.h @@ -11,7 +11,7 @@ namespace InputCommon { /// Initializes and registers all built-in input device factories. void Init(); -/// Unresisters all build-in input device factories and shut them down. +/// Deregisters all built-in input device factories and shuts them down. void Shutdown(); class Keyboard; diff --git a/src/input_common/sdl/sdl.cpp b/src/input_common/sdl/sdl.cpp index 756ee58b7..d404afa89 100644 --- a/src/input_common/sdl/sdl.cpp +++ b/src/input_common/sdl/sdl.cpp @@ -159,7 +159,7 @@ public: * - "axis"(optional): the index of the axis to bind * - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", * "down", "left" or "right" - * - "threshould"(only used for axis): a float value in (-1.0, 1.0) which the button is + * - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is * triggered if the axis value crosses * - "direction"(only used for axis): "+" means the button is triggered when the axis value * is greater than the threshold; "-" means the button is triggered when the axis value diff --git a/src/network/packet.cpp b/src/network/packet.cpp index 660e92c0d..cc60f2fbc 100644 --- a/src/network/packet.cpp +++ b/src/network/packet.cpp @@ -13,6 +13,18 @@ namespace Network { +#ifndef htonll +u64 htonll(u64 x) { + return ((1 == htonl(1)) ? (x) : ((uint64_t)htonl((x)&0xFFFFFFFF) << 32) | htonl((x) >> 32)); +} +#endif + +#ifndef ntohll +u64 ntohll(u64 x) { + return ((1 == ntohl(1)) ? (x) : ((uint64_t)ntohl((x)&0xFFFFFFFF) << 32) | ntohl((x) >> 32)); +} +#endif + void Packet::Append(const void* in_data, std::size_t size_in_bytes) { if (in_data && (size_in_bytes > 0)) { std::size_t start = data.size(); @@ -100,6 +112,20 @@ Packet& Packet::operator>>(u32& out_data) { return *this; } +Packet& Packet::operator>>(s64& out_data) { + s64 value; + Read(&value, sizeof(value)); + out_data = ntohll(value); + return *this; +} + +Packet& Packet::operator>>(u64& out_data) { + u64 value; + Read(&value, sizeof(value)); + out_data = ntohll(value); + return *this; +} + Packet& Packet::operator>>(float& out_data) { Read(&out_data, sizeof(out_data)); return *this; @@ -183,6 +209,18 @@ Packet& Packet::operator<<(u32 in_data) { return *this; } +Packet& Packet::operator<<(s64 in_data) { + s64 toWrite = htonll(in_data); + Append(&toWrite, sizeof(toWrite)); + return *this; +} + +Packet& Packet::operator<<(u64 in_data) { + u64 toWrite = htonll(in_data); + Append(&toWrite, sizeof(toWrite)); + return *this; +} + Packet& Packet::operator<<(float in_data) { Append(&in_data, sizeof(in_data)); return *this; diff --git a/src/network/packet.h b/src/network/packet.h index 94b351ab1..5a2e58dc2 100644 --- a/src/network/packet.h +++ b/src/network/packet.h @@ -72,6 +72,8 @@ public: Packet& operator>>(u16& out_data); Packet& operator>>(s32& out_data); Packet& operator>>(u32& out_data); + Packet& operator>>(s64& out_data); + Packet& operator>>(u64& out_data); Packet& operator>>(float& out_data); Packet& operator>>(double& out_data); Packet& operator>>(char* out_data); @@ -89,6 +91,8 @@ public: Packet& operator<<(u16 in_data); Packet& operator<<(s32 in_data); Packet& operator<<(u32 in_data); + Packet& operator<<(s64 in_data); + Packet& operator<<(u64 in_data); Packet& operator<<(float in_data); Packet& operator<<(double in_data); Packet& operator<<(const char* in_data); diff --git a/src/network/room.cpp b/src/network/room.cpp index fbbaf8b93..261049ab0 100644 --- a/src/network/room.cpp +++ b/src/network/room.cpp @@ -4,9 +4,9 @@ #include <algorithm> #include <atomic> +#include <mutex> #include <random> #include <thread> -#include <vector> #include "enet/enet.h" #include "network/packet.h" #include "network/room.h" @@ -29,12 +29,14 @@ public: struct Member { std::string nickname; ///< The nickname of the member. - std::string game_name; ///< The current game of the member + GameInfo game_info; ///< The current game of the member MacAddress mac_address; ///< The assigned mac address of the member. ENetPeer* peer; ///< The remote peer. }; using MemberList = std::vector<Member>; - MemberList members; ///< Information about the members of this room. + MemberList members; ///< Information about the members of this room + mutable std::mutex member_mutex; ///< Mutex for locking the members list + /// This should be a std::shared_mutex as soon as C++17 is supported RoomImpl() : random_gen(std::random_device()()), NintendoOUI{0x00, 0x1F, 0x32, 0x00, 0x00, 0x00} {} @@ -147,7 +149,7 @@ void Room::RoomImpl::ServerLoop() { case IdJoinRequest: HandleJoinRequest(&event); break; - case IdSetGameName: + case IdSetGameInfo: HandleGameNamePacket(&event); break; case IdWifiPacket: @@ -213,7 +215,10 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) { member.nickname = nickname; member.peer = event->peer; - members.push_back(std::move(member)); + { + std::lock_guard<std::mutex> lock(member_mutex); + members.push_back(std::move(member)); + } // Notify everyone that the room information has changed. BroadcastRoomInformation(); @@ -223,12 +228,14 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) { bool Room::RoomImpl::IsValidNickname(const std::string& nickname) const { // A nickname is valid if it is not already taken by anybody else in the room. // TODO(B3N30): Check for empty names, spaces, etc. + std::lock_guard<std::mutex> lock(member_mutex); return std::all_of(members.begin(), members.end(), [&nickname](const auto& member) { return member.nickname != nickname; }); } bool Room::RoomImpl::IsValidMacAddress(const MacAddress& address) const { // A MAC address is valid if it is not already taken by anybody else in the room. + std::lock_guard<std::mutex> lock(member_mutex); return std::all_of(members.begin(), members.end(), [&address](const auto& member) { return member.mac_address != address; }); } @@ -279,6 +286,7 @@ void Room::RoomImpl::SendCloseMessage() { packet << static_cast<u8>(IdCloseRoom); ENetPacket* enet_packet = enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); + std::lock_guard<std::mutex> lock(member_mutex); for (auto& member : members) { enet_peer_send(member.peer, 0, enet_packet); } @@ -295,10 +303,14 @@ void Room::RoomImpl::BroadcastRoomInformation() { packet << room_information.member_slots; packet << static_cast<u32>(members.size()); - for (const auto& member : members) { - packet << member.nickname; - packet << member.mac_address; - packet << member.game_name; + { + std::lock_guard<std::mutex> lock(member_mutex); + for (const auto& member : members) { + packet << member.nickname; + packet << member.mac_address; + packet << member.game_info.name; + packet << member.game_info.id; + } } ENetPacket* enet_packet = @@ -335,11 +347,13 @@ void Room::RoomImpl::HandleWifiPacket(const ENetEvent* event) { ENET_PACKET_FLAG_RELIABLE); if (destination_address == BroadcastMac) { // Send the data to everyone except the sender + std::lock_guard<std::mutex> lock(member_mutex); for (const auto& member : members) { if (member.peer != event->peer) enet_peer_send(member.peer, 0, enet_packet); } } else { // Send the data only to the destination client + std::lock_guard<std::mutex> lock(member_mutex); auto member = std::find_if(members.begin(), members.end(), [destination_address](const Member& member) -> bool { return member.mac_address == destination_address; @@ -361,6 +375,8 @@ void Room::RoomImpl::HandleChatPacket(const ENetEvent* event) { auto CompareNetworkAddress = [event](const Member member) -> bool { return member.peer == event->peer; }; + + std::lock_guard<std::mutex> lock(member_mutex); const auto sending_member = std::find_if(members.begin(), members.end(), CompareNetworkAddress); if (sending_member == members.end()) { return; // Received a chat message from a unknown sender @@ -385,22 +401,32 @@ void Room::RoomImpl::HandleGameNamePacket(const ENetEvent* event) { in_packet.Append(event->packet->data, event->packet->dataLength); in_packet.IgnoreBytes(sizeof(u8)); // Igonore the message type - std::string game_name; - in_packet >> game_name; - auto member = - std::find_if(members.begin(), members.end(), - [event](const Member& member) -> bool { return member.peer == event->peer; }); - if (member != members.end()) { - member->game_name = game_name; - BroadcastRoomInformation(); + GameInfo game_info; + in_packet >> game_info.name; + in_packet >> game_info.id; + + { + std::lock_guard<std::mutex> lock(member_mutex); + auto member = + std::find_if(members.begin(), members.end(), [event](const Member& member) -> bool { + return member.peer == event->peer; + }); + if (member != members.end()) { + member->game_info = game_info; + } } + BroadcastRoomInformation(); } void Room::RoomImpl::HandleClientDisconnection(ENetPeer* client) { // Remove the client from the members list. - members.erase(std::remove_if(members.begin(), members.end(), - [client](const Member& member) { return member.peer == client; }), - members.end()); + { + std::lock_guard<std::mutex> lock(member_mutex); + members.erase( + std::remove_if(members.begin(), members.end(), + [client](const Member& member) { return member.peer == client; }), + members.end()); + } // Announce the change to all clients. enet_peer_disconnect(client, 0); @@ -437,6 +463,19 @@ const RoomInformation& Room::GetRoomInformation() const { return room_impl->room_information; } +std::vector<Room::Member> Room::GetRoomMemberList() const { + std::vector<Room::Member> member_list; + std::lock_guard<std::mutex> lock(room_impl->member_mutex); + for (const auto& member_impl : room_impl->members) { + Member member; + member.nickname = member_impl.nickname; + member.mac_address = member_impl.mac_address; + member.game_info = member_impl.game_info; + member_list.push_back(member); + } + return member_list; +}; + void Room::Destroy() { room_impl->state = State::Closed; room_impl->room_thread->join(); @@ -447,7 +486,10 @@ void Room::Destroy() { } room_impl->room_information = {}; room_impl->server = nullptr; - room_impl->members.clear(); + { + std::lock_guard<std::mutex> lock(room_impl->member_mutex); + room_impl->members.clear(); + } room_impl->room_information.member_slots = 0; room_impl->room_information.name.clear(); } diff --git a/src/network/room.h b/src/network/room.h index 65b0d008a..8285a4d0c 100644 --- a/src/network/room.h +++ b/src/network/room.h @@ -7,6 +7,7 @@ #include <array> #include <memory> #include <string> +#include <vector> #include "common/common_types.h" namespace Network { @@ -21,6 +22,11 @@ struct RoomInformation { u32 member_slots; ///< Maximum number of members in this room }; +struct GameInfo { + std::string name{""}; + u64 id{0}; +}; + using MacAddress = std::array<u8, 6>; /// A special MAC address that tells the room we're joining to assign us a MAC address /// automatically. @@ -34,7 +40,7 @@ enum RoomMessageTypes : u8 { IdJoinRequest = 1, IdJoinSuccess, IdRoomInformation, - IdSetGameName, + IdSetGameInfo, IdWifiPacket, IdChatMessage, IdNameCollision, @@ -51,6 +57,12 @@ public: Closed, ///< The room is not opened and can not accept connections. }; + struct Member { + std::string nickname; ///< The nickname of the member. + GameInfo game_info; ///< The current game of the member + MacAddress mac_address; ///< The assigned mac address of the member. + }; + Room(); ~Room(); @@ -65,6 +77,11 @@ public: const RoomInformation& GetRoomInformation() const; /** + * Gets a list of the mbmers connected to the room. + */ + std::vector<Member> GetRoomMemberList() const; + + /** * Creates the socket for this room. Will bind to default address if * server is empty string. */ diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp index dac9bacae..f229ec6fd 100644 --- a/src/network/room_member.cpp +++ b/src/network/room_member.cpp @@ -5,6 +5,7 @@ #include <atomic> #include <list> #include <mutex> +#include <set> #include <thread> #include "common/assert.h" #include "enet/enet.h" @@ -25,6 +26,9 @@ public: /// Information about the room we're connected to. RoomInformation room_information; + /// The current game name, id and version + GameInfo current_game_info; + std::atomic<State> state{State::Idle}; ///< Current state of the RoomMember. void SetState(const State new_state); bool IsConnected() const; @@ -37,6 +41,24 @@ public: std::unique_ptr<std::thread> loop_thread; std::mutex send_list_mutex; ///< Mutex that controls access to the `send_list` variable. std::list<Packet> send_list; ///< A list that stores all packets to send the async + + template <typename T> + using CallbackSet = std::set<CallbackHandle<T>>; + std::mutex callback_mutex; ///< The mutex used for handling callbacks + + class Callbacks { + public: + template <typename T> + CallbackSet<T>& Get(); + + private: + CallbackSet<WifiPacket> callback_set_wifi_packet; + CallbackSet<ChatEntry> callback_set_chat_messages; + CallbackSet<RoomInformation> callback_set_room_information; + CallbackSet<State> callback_set_state; + }; + Callbacks callbacks; ///< All CallbackSets to all events + void MemberLoop(); void StartLoop(); @@ -84,12 +106,20 @@ public: * Disconnects the RoomMember from the Room */ void Disconnect(); + + template <typename T> + void Invoke(const T& data); + + template <typename T> + CallbackHandle<T> Bind(std::function<void(const T&)> callback); }; // RoomMemberImpl void RoomMember::RoomMemberImpl::SetState(const State new_state) { - state = new_state; - // TODO(B3N30): Invoke the callback functions + if (state != new_state) { + state = new_state; + Invoke<State>(state); + } } bool RoomMember::RoomMemberImpl::IsConnected() const { @@ -195,9 +225,10 @@ void RoomMember::RoomMemberImpl::HandleRoomInformationPacket(const ENetEvent* ev for (auto& member : member_information) { packet >> member.nickname; packet >> member.mac_address; - packet >> member.game_name; + packet >> member.game_info.name; + packet >> member.game_info.id; } - // TODO(B3N30): Invoke callbacks + Invoke(room_information); } void RoomMember::RoomMemberImpl::HandleJoinPacket(const ENetEvent* event) { @@ -209,7 +240,7 @@ void RoomMember::RoomMemberImpl::HandleJoinPacket(const ENetEvent* event) { // Parse the MAC Address from the packet packet >> mac_address; - // TODO(B3N30): Invoke callbacks + SetState(State::Joined); } void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) { @@ -235,7 +266,7 @@ void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) { packet >> wifi_packet.data; - // TODO(B3N30): Invoke callbacks + Invoke<WifiPacket>(wifi_packet); } void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) { @@ -248,7 +279,7 @@ void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) { ChatEntry chat_entry{}; packet >> chat_entry.nickname; packet >> chat_entry.message; - // TODO(B3N30): Invoke callbacks + Invoke<ChatEntry>(chat_entry); } void RoomMember::RoomMemberImpl::Disconnect() { @@ -276,6 +307,46 @@ void RoomMember::RoomMemberImpl::Disconnect() { server = nullptr; } +template <> +RoomMember::RoomMemberImpl::CallbackSet<WifiPacket>& RoomMember::RoomMemberImpl::Callbacks::Get() { + return callback_set_wifi_packet; +} + +template <> +RoomMember::RoomMemberImpl::CallbackSet<RoomMember::State>& +RoomMember::RoomMemberImpl::Callbacks::Get() { + return callback_set_state; +} + +template <> +RoomMember::RoomMemberImpl::CallbackSet<RoomInformation>& +RoomMember::RoomMemberImpl::Callbacks::Get() { + return callback_set_room_information; +} + +template <> +RoomMember::RoomMemberImpl::CallbackSet<ChatEntry>& RoomMember::RoomMemberImpl::Callbacks::Get() { + return callback_set_chat_messages; +} + +template <typename T> +void RoomMember::RoomMemberImpl::Invoke(const T& data) { + std::lock_guard<std::mutex> lock(callback_mutex); + CallbackSet<T> callback_set = callbacks.Get<T>(); + for (auto const& callback : callback_set) + (*callback)(data); +} + +template <typename T> +RoomMember::CallbackHandle<T> RoomMember::RoomMemberImpl::Bind( + std::function<void(const T&)> callback) { + std::lock_guard<std::mutex> lock(callback_mutex); + CallbackHandle<T> handle; + handle = std::make_shared<std::function<void(const T&)>>(callback); + callbacks.Get<T>().insert(handle); + return handle; +} + // RoomMember RoomMember::RoomMember() : room_member_impl{std::make_unique<RoomMemberImpl>()} { room_member_impl->client = enet_host_create(nullptr, 1, NumChannels, 0, 0); @@ -339,6 +410,7 @@ void RoomMember::Join(const std::string& nick, const char* server_addr, u16 serv room_member_impl->SetState(State::Joining); room_member_impl->StartLoop(); room_member_impl->SendJoinRequest(nick, preferred_mac); + SendGameInfo(room_member_impl->current_game_info); } else { room_member_impl->SetState(State::CouldNotConnect); } @@ -366,17 +438,53 @@ void RoomMember::SendChatMessage(const std::string& message) { room_member_impl->Send(std::move(packet)); } -void RoomMember::SendGameName(const std::string& game_name) { +void RoomMember::SendGameInfo(const GameInfo& game_info) { + room_member_impl->current_game_info = game_info; + if (!IsConnected()) + return; + Packet packet; - packet << static_cast<u8>(IdSetGameName); - packet << game_name; + packet << static_cast<u8>(IdSetGameInfo); + packet << game_info.name; + packet << game_info.id; room_member_impl->Send(std::move(packet)); } +RoomMember::CallbackHandle<RoomMember::State> RoomMember::BindOnStateChanged( + std::function<void(const RoomMember::State&)> callback) { + return room_member_impl->Bind(callback); +} + +RoomMember::CallbackHandle<WifiPacket> RoomMember::BindOnWifiPacketReceived( + std::function<void(const WifiPacket&)> callback) { + return room_member_impl->Bind(callback); +} + +RoomMember::CallbackHandle<RoomInformation> RoomMember::BindOnRoomInformationChanged( + std::function<void(const RoomInformation&)> callback) { + return room_member_impl->Bind(callback); +} + +RoomMember::CallbackHandle<ChatEntry> RoomMember::BindOnChatMessageRecieved( + std::function<void(const ChatEntry&)> callback) { + return room_member_impl->Bind(callback); +} + +template <typename T> +void RoomMember::Unbind(CallbackHandle<T> handle) { + std::lock_guard<std::mutex> lock(room_member_impl->callback_mutex); + room_member_impl->callbacks.Get<T>().erase(handle); +} + void RoomMember::Leave() { room_member_impl->SetState(State::Idle); room_member_impl->loop_thread->join(); room_member_impl->loop_thread.reset(); } +template void RoomMember::Unbind(CallbackHandle<WifiPacket>); +template void RoomMember::Unbind(CallbackHandle<RoomMember::State>); +template void RoomMember::Unbind(CallbackHandle<RoomInformation>); +template void RoomMember::Unbind(CallbackHandle<ChatEntry>); + } // namespace Network diff --git a/src/network/room_member.h b/src/network/room_member.h index bc1af3a7e..98770a234 100644 --- a/src/network/room_member.h +++ b/src/network/room_member.h @@ -4,6 +4,7 @@ #pragma once +#include <functional> #include <memory> #include <string> #include <vector> @@ -53,12 +54,23 @@ public: struct MemberInformation { std::string nickname; ///< Nickname of the member. - std::string game_name; ///< Name of the game they're currently playing, or empty if they're + GameInfo game_info; ///< Name of the game they're currently playing, or empty if they're /// not playing anything. MacAddress mac_address; ///< MAC address associated with this member. }; using MemberList = std::vector<MemberInformation>; + // The handle for the callback functions + template <typename T> + using CallbackHandle = std::shared_ptr<std::function<void(const T&)>>; + + /** + * Unbinds a callback function from the events. + * @param handle The connection handle to disconnect + */ + template <typename T> + void Unbind(CallbackHandle<T> handle); + RoomMember(); ~RoomMember(); @@ -113,10 +125,49 @@ public: void SendChatMessage(const std::string& message); /** - * Sends the current game name to the room. - * @param game_name The game name. + * Sends the current game info to the room. + * @param game_info The game information. + */ + void SendGameInfo(const GameInfo& game_info); + + /** + * Binds a function to an event that will be triggered every time the State of the member + * changed. The function wil be called every time the event is triggered. The callback function + * must not bind or unbind a function. Doing so will cause a deadlock + * @param callback The function to call + * @return A handle used for removing the function from the registered list + */ + CallbackHandle<State> BindOnStateChanged(std::function<void(const State&)> callback); + + /** + * Binds a function to an event that will be triggered every time a WifiPacket is received. + * The function wil be called everytime the event is triggered. + * The callback function must not bind or unbind a function. Doing so will cause a deadlock + * @param callback The function to call + * @return A handle used for removing the function from the registered list + */ + CallbackHandle<WifiPacket> BindOnWifiPacketReceived( + std::function<void(const WifiPacket&)> callback); + + /** + * Binds a function to an event that will be triggered every time the RoomInformation changes. + * The function wil be called every time the event is triggered. + * The callback function must not bind or unbind a function. Doing so will cause a deadlock + * @param callback The function to call + * @return A handle used for removing the function from the registered list + */ + CallbackHandle<RoomInformation> BindOnRoomInformationChanged( + std::function<void(const RoomInformation&)> callback); + + /** + * Binds a function to an event that will be triggered every time a ChatMessage is received. + * The function wil be called every time the event is triggered. + * The callback function must not bind or unbind a function. Doing so will cause a deadlock + * @param callback The function to call + * @return A handle used for removing the function from the registered list */ - void SendGameName(const std::string& game_name); + CallbackHandle<ChatEntry> BindOnChatMessageRecieved( + std::function<void(const ChatEntry&)> callback); /** * Leaves the current room. diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index bb192affd..ae67aab05 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -525,11 +525,12 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { "float geo_factor = 1.0;\n"; // Compute fragment normals and tangents - const std::string pertubation = - "2.0 * (" + SampleTexture(config, lighting.bump_selector) + ").rgb - 1.0"; + auto Perturbation = [&]() { + return "2.0 * (" + SampleTexture(config, lighting.bump_selector) + ").rgb - 1.0"; + }; if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) { // Bump mapping is enabled using a normal map - out += "vec3 surface_normal = " + pertubation + ";\n"; + out += "vec3 surface_normal = " + Perturbation() + ";\n"; // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher // precision result @@ -543,7 +544,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { out += "vec3 surface_tangent = vec3(1.0, 0.0, 0.0);\n"; } else if (lighting.bump_mode == LightingRegs::LightingBumpMode::TangentMap) { // Bump mapping is enabled using a tangent map - out += "vec3 surface_tangent = " + pertubation + ";\n"; + out += "vec3 surface_tangent = " + Perturbation() + ";\n"; // Mathematically, recomputing Z-component of the tangent vector won't affect the relevant // computation below, which is also confirmed on 3DS. So we don't bother recomputing here // even if 'renorm' is enabled. |