summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/core.cpp5
-rw-r--r--src/core/hle/service/apt/apt.cpp229
-rw-r--r--src/core/hle/service/apt/apt.h6
-rw-r--r--src/core/hle/service/dsp_dsp.cpp7
-rw-r--r--src/core/loader/ncch.cpp8
-rw-r--r--src/input_common/main.h2
-rw-r--r--src/network/packet.cpp38
-rw-r--r--src/network/packet.h4
-rw-r--r--src/network/room.cpp84
-rw-r--r--src/network/room.h19
-rw-r--r--src/network/room_member.cpp128
-rw-r--r--src/network/room_member.h59
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp9
-rw-r--r--src/video_core/swrasterizer/lighting.cpp22
15 files changed, 539 insertions, 83 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index ea36b7d95..53bd50eb2 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -386,7 +386,7 @@ set(HEADERS
386 386
387create_directory_groups(${SRCS} ${HEADERS}) 387create_directory_groups(${SRCS} ${HEADERS})
388add_library(core STATIC ${SRCS} ${HEADERS}) 388add_library(core STATIC ${SRCS} ${HEADERS})
389target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) 389target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core)
390target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt) 390target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt)
391if (ENABLE_WEB_SERVICE) 391if (ENABLE_WEB_SERVICE)
392 target_link_libraries(core PUBLIC json-headers web_service) 392 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 @@
19#include "core/loader/loader.h" 19#include "core/loader/loader.h"
20#include "core/memory_setup.h" 20#include "core/memory_setup.h"
21#include "core/settings.h" 21#include "core/settings.h"
22#include "network/network.h"
22#include "video_core/video_core.h" 23#include "video_core/video_core.h"
23 24
24namespace Core { 25namespace Core {
@@ -188,6 +189,10 @@ void System::Shutdown() {
188 cpu_core = nullptr; 189 cpu_core = nullptr;
189 app_loader = nullptr; 190 app_loader = nullptr;
190 telemetry_session = nullptr; 191 telemetry_session = nullptr;
192 if (auto room_member = Network::GetRoomMember().lock()) {
193 Network::GameInfo game_info{};
194 room_member->SendGameInfo(game_info);
195 }
191 196
192 LOG_DEBUG(Core, "Shutdown OK"); 197 LOG_DEBUG(Core, "Shutdown OK");
193} 198}
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;
34static bool shared_font_relocated = false; 34static bool shared_font_relocated = false;
35 35
36static Kernel::SharedPtr<Kernel::Mutex> lock; 36static Kernel::SharedPtr<Kernel::Mutex> lock;
37static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event
38static Kernel::SharedPtr<Kernel::Event> parameter_event; ///< APT parameter event
39 37
40static u32 cpu_percent; ///< CPU time available to the running application 38static u32 cpu_percent; ///< CPU time available to the running application
41 39
@@ -44,32 +42,160 @@ static u8 unknown_ns_state_field;
44 42
45static ScreencapPostPermission screen_capture_post_permission; 43static ScreencapPostPermission screen_capture_post_permission;
46 44
47/// Parameter data to be returned in the next call to Glance/ReceiveParameter 45/// Parameter data to be returned in the next call to Glance/ReceiveParameter.
46/// TODO(Subv): Use std::optional once we migrate to C++17.
48static boost::optional<MessageParameter> next_parameter; 47static boost::optional<MessageParameter> next_parameter;
49 48
49enum class AppletPos { Application = 0, Library = 1, System = 2, SysLibrary = 3, Resident = 4 };
50
51static constexpr size_t NumAppletSlot = 4;
52
53enum class AppletSlot : u8 {
54 Application,
55 SystemApplet,
56 HomeMenu,
57 LibraryApplet,
58
59 // An invalid tag
60 Error,
61};
62
63union AppletAttributes {
64 u32 raw;
65
66 BitField<0, 3, u32> applet_pos;
67
68 AppletAttributes() : raw(0) {}
69 AppletAttributes(u32 attributes) : raw(attributes) {}
70};
71
72struct AppletSlotData {
73 AppletId applet_id;
74 AppletSlot slot;
75 bool registered;
76 AppletAttributes attributes;
77 Kernel::SharedPtr<Kernel::Event> notification_event;
78 Kernel::SharedPtr<Kernel::Event> parameter_event;
79};
80
81// Holds data about the concurrently running applets in the system.
82static std::array<AppletSlotData, NumAppletSlot> applet_slots = {};
83
84// This overload returns nullptr if no applet with the specified id has been started.
85static AppletSlotData* GetAppletSlotData(AppletId id) {
86 auto GetSlot = [](AppletSlot slot) -> AppletSlotData* {
87 return &applet_slots[static_cast<size_t>(slot)];
88 };
89
90 if (id == AppletId::Application) {
91 auto* slot = GetSlot(AppletSlot::Application);
92 if (slot->applet_id != AppletId::None)
93 return slot;
94
95 return nullptr;
96 }
97
98 if (id == AppletId::AnySystemApplet) {
99 auto* system_slot = GetSlot(AppletSlot::SystemApplet);
100 if (system_slot->applet_id != AppletId::None)
101 return system_slot;
102
103 // The Home Menu is also a system applet, but it lives in its own slot to be able to run
104 // concurrently with other system applets.
105 auto* home_slot = GetSlot(AppletSlot::HomeMenu);
106 if (home_slot->applet_id != AppletId::None)
107 return home_slot;
108
109 return nullptr;
110 }
111
112 if (id == AppletId::AnyLibraryApplet || id == AppletId::AnySysLibraryApplet) {
113 auto* slot = GetSlot(AppletSlot::LibraryApplet);
114 if (slot->applet_id == AppletId::None)
115 return nullptr;
116
117 u32 applet_pos = slot->attributes.applet_pos;
118
119 if (id == AppletId::AnyLibraryApplet && applet_pos == static_cast<u32>(AppletPos::Library))
120 return slot;
121
122 if (id == AppletId::AnySysLibraryApplet &&
123 applet_pos == static_cast<u32>(AppletPos::SysLibrary))
124 return slot;
125
126 return nullptr;
127 }
128
129 if (id == AppletId::HomeMenu || id == AppletId::AlternateMenu) {
130 auto* slot = GetSlot(AppletSlot::HomeMenu);
131 if (slot->applet_id != AppletId::None)
132 return slot;
133
134 return nullptr;
135 }
136
137 for (auto& slot : applet_slots) {
138 if (slot.applet_id == id)
139 return &slot;
140 }
141
142 return nullptr;
143}
144
145static AppletSlotData* GetAppletSlotData(AppletAttributes attributes) {
146 // Mapping from AppletPos to AppletSlot
147 static constexpr std::array<AppletSlot, 6> applet_position_slots = {
148 AppletSlot::Application, AppletSlot::LibraryApplet, AppletSlot::SystemApplet,
149 AppletSlot::LibraryApplet, AppletSlot::Error, AppletSlot::LibraryApplet};
150
151 u32 applet_pos = attributes.applet_pos;
152 if (applet_pos >= applet_position_slots.size())
153 return nullptr;
154
155 AppletSlot slot = applet_position_slots[applet_pos];
156
157 if (slot == AppletSlot::Error)
158 return nullptr;
159
160 return &applet_slots[static_cast<size_t>(slot)];
161}
162
50void SendParameter(const MessageParameter& parameter) { 163void SendParameter(const MessageParameter& parameter) {
51 next_parameter = parameter; 164 next_parameter = parameter;
52 // Signal the event to let the application know that a new parameter is ready to be read 165 // Signal the event to let the receiver know that a new parameter is ready to be read
53 parameter_event->Signal(); 166 auto* const slot_data = GetAppletSlotData(static_cast<AppletId>(parameter.destination_id));
167 ASSERT(slot_data);
168
169 slot_data->parameter_event->Signal();
54} 170}
55 171
56void Initialize(Service::Interface* self) { 172void Initialize(Service::Interface* self) {
57 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2, 2, 0); // 0x20080 173 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2, 2, 0); // 0x20080
58 u32 app_id = rp.Pop<u32>(); 174 u32 app_id = rp.Pop<u32>();
59 u32 flags = rp.Pop<u32>(); 175 u32 attributes = rp.Pop<u32>();
60 IPC::RequestBuilder rb = rp.MakeBuilder(1, 3);
61 rb.Push(RESULT_SUCCESS);
62 rb.PushCopyHandles(Kernel::g_handle_table.Create(notification_event).Unwrap(),
63 Kernel::g_handle_table.Create(parameter_event).Unwrap());
64 176
65 // TODO(bunnei): Check if these events are cleared every time Initialize is called. 177 LOG_DEBUG(Service_APT, "called app_id=0x%08X, attributes=0x%08X", app_id, attributes);
66 notification_event->Clear(); 178
67 parameter_event->Clear(); 179 auto* const slot_data = GetAppletSlotData(attributes);
180
181 // Note: The real NS service does not check if the attributes value is valid before accessing
182 // the data in the array
183 ASSERT_MSG(slot_data, "Invalid application attributes");
184
185 if (slot_data->registered) {
186 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
187 rb.Push(ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet,
188 ErrorSummary::InvalidState, ErrorLevel::Status));
189 return;
190 }
68 191
69 ASSERT_MSG((nullptr != lock), "Cannot initialize without lock"); 192 slot_data->applet_id = static_cast<AppletId>(app_id);
70 lock->Release(); 193 slot_data->attributes.raw = attributes;
71 194
72 LOG_DEBUG(Service_APT, "called app_id=0x%08X, flags=0x%08X", app_id, flags); 195 IPC::RequestBuilder rb = rp.MakeBuilder(1, 3);
196 rb.Push(RESULT_SUCCESS);
197 rb.PushCopyHandles(Kernel::g_handle_table.Create(slot_data->notification_event).Unwrap(),
198 Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap());
73} 199}
74 200
75void GetSharedFont(Service::Interface* self) { 201void GetSharedFont(Service::Interface* self) {
@@ -120,7 +246,12 @@ void GetLockHandle(Service::Interface* self) {
120 // this will cause the app to wait until parameter_event is signaled. 246 // this will cause the app to wait until parameter_event is signaled.
121 u32 applet_attributes = rp.Pop<u32>(); 247 u32 applet_attributes = rp.Pop<u32>();
122 IPC::RequestBuilder rb = rp.MakeBuilder(3, 2); 248 IPC::RequestBuilder rb = rp.MakeBuilder(3, 2);
123 rb.Push(RESULT_SUCCESS); // No error 249 rb.Push(RESULT_SUCCESS); // No error
250
251 // TODO(Subv): The output attributes should have an AppletPos of either Library or System |
252 // Library (depending on the type of the last launched applet) if the input attributes'
253 // AppletPos has the Library bit set.
254
124 rb.Push(applet_attributes); // Applet Attributes, this value is passed to Enable. 255 rb.Push(applet_attributes); // Applet Attributes, this value is passed to Enable.
125 rb.Push<u32>(0); // Least significant bit = power button state 256 rb.Push<u32>(0); // Least significant bit = power button state
126 Kernel::Handle handle_copy = Kernel::g_handle_table.Create(lock).Unwrap(); 257 Kernel::Handle handle_copy = Kernel::g_handle_table.Create(lock).Unwrap();
@@ -133,10 +264,22 @@ void GetLockHandle(Service::Interface* self) {
133void Enable(Service::Interface* self) { 264void Enable(Service::Interface* self) {
134 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3, 1, 0); // 0x30040 265 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3, 1, 0); // 0x30040
135 u32 attributes = rp.Pop<u32>(); 266 u32 attributes = rp.Pop<u32>();
267
268 LOG_DEBUG(Service_APT, "called attributes=0x%08X", attributes);
269
136 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); 270 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
137 rb.Push(RESULT_SUCCESS); // No error 271
138 parameter_event->Signal(); // Let the application know that it has been started 272 auto* const slot_data = GetAppletSlotData(attributes);
139 LOG_WARNING(Service_APT, "(STUBBED) called attributes=0x%08X", attributes); 273
274 if (!slot_data) {
275 rb.Push(ResultCode(ErrCodes::InvalidAppletSlot, ErrorModule::Applet,
276 ErrorSummary::InvalidState, ErrorLevel::Status));
277 return;
278 }
279
280 slot_data->registered = true;
281
282 rb.Push(RESULT_SUCCESS);
140} 283}
141 284
142void GetAppletManInfo(Service::Interface* self) { 285void GetAppletManInfo(Service::Interface* self) {
@@ -154,22 +297,27 @@ void GetAppletManInfo(Service::Interface* self) {
154 297
155void IsRegistered(Service::Interface* self) { 298void IsRegistered(Service::Interface* self) {
156 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x9, 1, 0); // 0x90040 299 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x9, 1, 0); // 0x90040
157 u32 app_id = rp.Pop<u32>(); 300 AppletId app_id = static_cast<AppletId>(rp.Pop<u32>());
158 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); 301 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
159 rb.Push(RESULT_SUCCESS); // No error 302 rb.Push(RESULT_SUCCESS); // No error
160 303
161 // TODO(Subv): An application is considered "registered" if it has already called APT::Enable 304 auto* const slot_data = GetAppletSlotData(app_id);
162 // handle this properly once we implement multiprocess support. 305
163 bool is_registered = false; // Set to not registered by default 306 // Check if an LLE applet was registered first, then fallback to HLE applets
307 bool is_registered = slot_data && slot_data->registered;
164 308
165 if (app_id == static_cast<u32>(AppletId::AnyLibraryApplet)) { 309 if (!is_registered) {
166 is_registered = HLE::Applets::IsLibraryAppletRunning(); 310 if (app_id == AppletId::AnyLibraryApplet) {
167 } else if (auto applet = HLE::Applets::Applet::Get(static_cast<AppletId>(app_id))) { 311 is_registered = HLE::Applets::IsLibraryAppletRunning();
168 is_registered = true; // Set to registered 312 } else if (auto applet = HLE::Applets::Applet::Get(app_id)) {
313 // The applet exists, set it as registered.
314 is_registered = true;
315 }
169 } 316 }
317
170 rb.Push(is_registered); 318 rb.Push(is_registered);
171 319
172 LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id); 320 LOG_DEBUG(Service_APT, "called app_id=0x%08X", static_cast<u32>(app_id));
173} 321}
174 322
175void InquireNotification(Service::Interface* self) { 323void InquireNotification(Service::Interface* self) {
@@ -864,14 +1012,23 @@ void Init() {
864 screen_capture_post_permission = 1012 screen_capture_post_permission =
865 ScreencapPostPermission::CleanThePermission; // TODO(JamePeng): verify the initial value 1013 ScreencapPostPermission::CleanThePermission; // TODO(JamePeng): verify the initial value
866 1014
867 // TODO(bunnei): Check if these are created in Initialize or on APT process startup. 1015 for (size_t slot = 0; slot < applet_slots.size(); ++slot) {
868 notification_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Notification"); 1016 auto& slot_data = applet_slots[slot];
869 parameter_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Start"); 1017 slot_data.slot = static_cast<AppletSlot>(slot);
1018 slot_data.applet_id = AppletId::None;
1019 slot_data.attributes.raw = 0;
1020 slot_data.registered = false;
1021 slot_data.notification_event =
1022 Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Notification");
1023 slot_data.parameter_event =
1024 Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Parameter");
1025 }
870 1026
871 // Initialize the parameter to wake up the application. 1027 // Initialize the parameter to wake up the application.
872 next_parameter.emplace(); 1028 next_parameter.emplace();
873 next_parameter->signal = static_cast<u32>(SignalType::Wakeup); 1029 next_parameter->signal = static_cast<u32>(SignalType::Wakeup);
874 next_parameter->destination_id = static_cast<u32>(AppletId::Application); 1030 next_parameter->destination_id = static_cast<u32>(AppletId::Application);
1031 applet_slots[static_cast<size_t>(AppletSlot::Application)].parameter_event->Signal();
875} 1032}
876 1033
877void Shutdown() { 1034void Shutdown() {
@@ -879,8 +1036,12 @@ void Shutdown() {
879 shared_font_loaded = false; 1036 shared_font_loaded = false;
880 shared_font_relocated = false; 1037 shared_font_relocated = false;
881 lock = nullptr; 1038 lock = nullptr;
882 notification_event = nullptr; 1039
883 parameter_event = nullptr; 1040 for (auto& slot : applet_slots) {
1041 slot.registered = false;
1042 slot.notification_event = nullptr;
1043 slot.parameter_event = nullptr;
1044 }
884 1045
885 next_parameter = boost::none; 1046 next_parameter = boost::none;
886 1047
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 {
72 72
73/// App Id's used by APT functions 73/// App Id's used by APT functions
74enum class AppletId : u32 { 74enum class AppletId : u32 {
75 None = 0,
76 AnySystemApplet = 0x100,
75 HomeMenu = 0x101, 77 HomeMenu = 0x101,
76 AlternateMenu = 0x103, 78 AlternateMenu = 0x103,
77 Camera = 0x110, 79 Camera = 0x110,
@@ -83,6 +85,7 @@ enum class AppletId : u32 {
83 Miiverse = 0x117, 85 Miiverse = 0x117,
84 MiiversePost = 0x118, 86 MiiversePost = 0x118,
85 AmiiboSettings = 0x119, 87 AmiiboSettings = 0x119,
88 AnySysLibraryApplet = 0x200,
86 SoftwareKeyboard1 = 0x201, 89 SoftwareKeyboard1 = 0x201,
87 Ed1 = 0x202, 90 Ed1 = 0x202,
88 PnoteApp = 0x204, 91 PnoteApp = 0x204,
@@ -119,8 +122,9 @@ enum class ScreencapPostPermission : u32 {
119namespace ErrCodes { 122namespace ErrCodes {
120enum { 123enum {
121 ParameterPresent = 2, 124 ParameterPresent = 2,
125 InvalidAppletSlot = 4,
122}; 126};
123} 127} // namespace ErrCodes
124 128
125/// Send a parameter to the currently-running application, which will read it via ReceiveParameter 129/// Send a parameter to the currently-running application, which will read it via ReceiveParameter
126void SendParameter(const MessageParameter& parameter); 130void 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) {
147 LOG_INFO(Service_DSP, "Firmware hash: %#" PRIx64, 147 LOG_INFO(Service_DSP, "Firmware hash: %#" PRIx64,
148 Common::ComputeHash64(component_data.data(), component_data.size())); 148 Common::ComputeHash64(component_data.data(), component_data.size()));
149 // Some versions of the firmware have the location of DSP structures listed here. 149 // Some versions of the firmware have the location of DSP structures listed here.
150 ASSERT(size > 0x37C); 150 if (size > 0x37C) {
151 LOG_INFO(Service_DSP, "Structures hash: %#" PRIx64, 151 LOG_INFO(Service_DSP, "Structures hash: %#" PRIx64,
152 Common::ComputeHash64(component_data.data() + 0x340, 60)); 152 Common::ComputeHash64(component_data.data() + 0x340, 60));
153 }
153 154
154 LOG_WARNING(Service_DSP, 155 LOG_WARNING(Service_DSP,
155 "(STUBBED) called size=0x%X, prog_mask=0x%08X, data_mask=0x%08X, buffer=0x%08X", 156 "(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 @@
20#include "core/loader/ncch.h" 20#include "core/loader/ncch.h"
21#include "core/loader/smdh.h" 21#include "core/loader/smdh.h"
22#include "core/memory.h" 22#include "core/memory.h"
23#include "network/network.h"
23 24
24//////////////////////////////////////////////////////////////////////////////////////////////////// 25////////////////////////////////////////////////////////////////////////////////////////////////////
25// Loader namespace 26// Loader namespace
@@ -350,6 +351,13 @@ ResultStatus AppLoader_NCCH::Load() {
350 351
351 Core::Telemetry().AddField(Telemetry::FieldType::Session, "ProgramId", program_id); 352 Core::Telemetry().AddField(Telemetry::FieldType::Session, "ProgramId", program_id);
352 353
354 if (auto room_member = Network::GetRoomMember().lock()) {
355 Network::GameInfo game_info;
356 ReadTitle(game_info.name);
357 game_info.id = ncch_header.program_id;
358 room_member->SendGameInfo(game_info);
359 }
360
353 is_loaded = true; // Set state to loaded 361 is_loaded = true; // Set state to loaded
354 362
355 result = LoadExec(); // Load the executable into memory for booting 363 result = LoadExec(); // Load the executable into memory for booting
diff --git a/src/input_common/main.h b/src/input_common/main.h
index 74283f7c6..5604f0fa8 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -11,7 +11,7 @@ namespace InputCommon {
11/// Initializes and registers all built-in input device factories. 11/// Initializes and registers all built-in input device factories.
12void Init(); 12void Init();
13 13
14/// Unresisters all build-in input device factories and shut them down. 14/// Deregisters all built-in input device factories and shuts them down.
15void Shutdown(); 15void Shutdown();
16 16
17class Keyboard; 17class Keyboard;
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 @@
13 13
14namespace Network { 14namespace Network {
15 15
16#ifndef htonll
17u64 htonll(u64 x) {
18 return ((1 == htonl(1)) ? (x) : ((uint64_t)htonl((x)&0xFFFFFFFF) << 32) | htonl((x) >> 32));
19}
20#endif
21
22#ifndef ntohll
23u64 ntohll(u64 x) {
24 return ((1 == ntohl(1)) ? (x) : ((uint64_t)ntohl((x)&0xFFFFFFFF) << 32) | ntohl((x) >> 32));
25}
26#endif
27
16void Packet::Append(const void* in_data, std::size_t size_in_bytes) { 28void Packet::Append(const void* in_data, std::size_t size_in_bytes) {
17 if (in_data && (size_in_bytes > 0)) { 29 if (in_data && (size_in_bytes > 0)) {
18 std::size_t start = data.size(); 30 std::size_t start = data.size();
@@ -100,6 +112,20 @@ Packet& Packet::operator>>(u32& out_data) {
100 return *this; 112 return *this;
101} 113}
102 114
115Packet& Packet::operator>>(s64& out_data) {
116 s64 value;
117 Read(&value, sizeof(value));
118 out_data = ntohll(value);
119 return *this;
120}
121
122Packet& Packet::operator>>(u64& out_data) {
123 u64 value;
124 Read(&value, sizeof(value));
125 out_data = ntohll(value);
126 return *this;
127}
128
103Packet& Packet::operator>>(float& out_data) { 129Packet& Packet::operator>>(float& out_data) {
104 Read(&out_data, sizeof(out_data)); 130 Read(&out_data, sizeof(out_data));
105 return *this; 131 return *this;
@@ -183,6 +209,18 @@ Packet& Packet::operator<<(u32 in_data) {
183 return *this; 209 return *this;
184} 210}
185 211
212Packet& Packet::operator<<(s64 in_data) {
213 s64 toWrite = htonll(in_data);
214 Append(&toWrite, sizeof(toWrite));
215 return *this;
216}
217
218Packet& Packet::operator<<(u64 in_data) {
219 u64 toWrite = htonll(in_data);
220 Append(&toWrite, sizeof(toWrite));
221 return *this;
222}
223
186Packet& Packet::operator<<(float in_data) { 224Packet& Packet::operator<<(float in_data) {
187 Append(&in_data, sizeof(in_data)); 225 Append(&in_data, sizeof(in_data));
188 return *this; 226 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:
72 Packet& operator>>(u16& out_data); 72 Packet& operator>>(u16& out_data);
73 Packet& operator>>(s32& out_data); 73 Packet& operator>>(s32& out_data);
74 Packet& operator>>(u32& out_data); 74 Packet& operator>>(u32& out_data);
75 Packet& operator>>(s64& out_data);
76 Packet& operator>>(u64& out_data);
75 Packet& operator>>(float& out_data); 77 Packet& operator>>(float& out_data);
76 Packet& operator>>(double& out_data); 78 Packet& operator>>(double& out_data);
77 Packet& operator>>(char* out_data); 79 Packet& operator>>(char* out_data);
@@ -89,6 +91,8 @@ public:
89 Packet& operator<<(u16 in_data); 91 Packet& operator<<(u16 in_data);
90 Packet& operator<<(s32 in_data); 92 Packet& operator<<(s32 in_data);
91 Packet& operator<<(u32 in_data); 93 Packet& operator<<(u32 in_data);
94 Packet& operator<<(s64 in_data);
95 Packet& operator<<(u64 in_data);
92 Packet& operator<<(float in_data); 96 Packet& operator<<(float in_data);
93 Packet& operator<<(double in_data); 97 Packet& operator<<(double in_data);
94 Packet& operator<<(const char* in_data); 98 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 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <atomic> 6#include <atomic>
7#include <mutex>
7#include <random> 8#include <random>
8#include <thread> 9#include <thread>
9#include <vector>
10#include "enet/enet.h" 10#include "enet/enet.h"
11#include "network/packet.h" 11#include "network/packet.h"
12#include "network/room.h" 12#include "network/room.h"
@@ -29,12 +29,14 @@ public:
29 29
30 struct Member { 30 struct Member {
31 std::string nickname; ///< The nickname of the member. 31 std::string nickname; ///< The nickname of the member.
32 std::string game_name; ///< The current game of the member 32 GameInfo game_info; ///< The current game of the member
33 MacAddress mac_address; ///< The assigned mac address of the member. 33 MacAddress mac_address; ///< The assigned mac address of the member.
34 ENetPeer* peer; ///< The remote peer. 34 ENetPeer* peer; ///< The remote peer.
35 }; 35 };
36 using MemberList = std::vector<Member>; 36 using MemberList = std::vector<Member>;
37 MemberList members; ///< Information about the members of this room. 37 MemberList members; ///< Information about the members of this room
38 mutable std::mutex member_mutex; ///< Mutex for locking the members list
39 /// This should be a std::shared_mutex as soon as C++17 is supported
38 40
39 RoomImpl() 41 RoomImpl()
40 : random_gen(std::random_device()()), NintendoOUI{0x00, 0x1F, 0x32, 0x00, 0x00, 0x00} {} 42 : random_gen(std::random_device()()), NintendoOUI{0x00, 0x1F, 0x32, 0x00, 0x00, 0x00} {}
@@ -147,7 +149,7 @@ void Room::RoomImpl::ServerLoop() {
147 case IdJoinRequest: 149 case IdJoinRequest:
148 HandleJoinRequest(&event); 150 HandleJoinRequest(&event);
149 break; 151 break;
150 case IdSetGameName: 152 case IdSetGameInfo:
151 HandleGameNamePacket(&event); 153 HandleGameNamePacket(&event);
152 break; 154 break;
153 case IdWifiPacket: 155 case IdWifiPacket:
@@ -213,7 +215,10 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
213 member.nickname = nickname; 215 member.nickname = nickname;
214 member.peer = event->peer; 216 member.peer = event->peer;
215 217
216 members.push_back(std::move(member)); 218 {
219 std::lock_guard<std::mutex> lock(member_mutex);
220 members.push_back(std::move(member));
221 }
217 222
218 // Notify everyone that the room information has changed. 223 // Notify everyone that the room information has changed.
219 BroadcastRoomInformation(); 224 BroadcastRoomInformation();
@@ -223,12 +228,14 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
223bool Room::RoomImpl::IsValidNickname(const std::string& nickname) const { 228bool Room::RoomImpl::IsValidNickname(const std::string& nickname) const {
224 // A nickname is valid if it is not already taken by anybody else in the room. 229 // A nickname is valid if it is not already taken by anybody else in the room.
225 // TODO(B3N30): Check for empty names, spaces, etc. 230 // TODO(B3N30): Check for empty names, spaces, etc.
231 std::lock_guard<std::mutex> lock(member_mutex);
226 return std::all_of(members.begin(), members.end(), 232 return std::all_of(members.begin(), members.end(),
227 [&nickname](const auto& member) { return member.nickname != nickname; }); 233 [&nickname](const auto& member) { return member.nickname != nickname; });
228} 234}
229 235
230bool Room::RoomImpl::IsValidMacAddress(const MacAddress& address) const { 236bool Room::RoomImpl::IsValidMacAddress(const MacAddress& address) const {
231 // A MAC address is valid if it is not already taken by anybody else in the room. 237 // A MAC address is valid if it is not already taken by anybody else in the room.
238 std::lock_guard<std::mutex> lock(member_mutex);
232 return std::all_of(members.begin(), members.end(), 239 return std::all_of(members.begin(), members.end(),
233 [&address](const auto& member) { return member.mac_address != address; }); 240 [&address](const auto& member) { return member.mac_address != address; });
234} 241}
@@ -279,6 +286,7 @@ void Room::RoomImpl::SendCloseMessage() {
279 packet << static_cast<u8>(IdCloseRoom); 286 packet << static_cast<u8>(IdCloseRoom);
280 ENetPacket* enet_packet = 287 ENetPacket* enet_packet =
281 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); 288 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
289 std::lock_guard<std::mutex> lock(member_mutex);
282 for (auto& member : members) { 290 for (auto& member : members) {
283 enet_peer_send(member.peer, 0, enet_packet); 291 enet_peer_send(member.peer, 0, enet_packet);
284 } 292 }
@@ -295,10 +303,14 @@ void Room::RoomImpl::BroadcastRoomInformation() {
295 packet << room_information.member_slots; 303 packet << room_information.member_slots;
296 304
297 packet << static_cast<u32>(members.size()); 305 packet << static_cast<u32>(members.size());
298 for (const auto& member : members) { 306 {
299 packet << member.nickname; 307 std::lock_guard<std::mutex> lock(member_mutex);
300 packet << member.mac_address; 308 for (const auto& member : members) {
301 packet << member.game_name; 309 packet << member.nickname;
310 packet << member.mac_address;
311 packet << member.game_info.name;
312 packet << member.game_info.id;
313 }
302 } 314 }
303 315
304 ENetPacket* enet_packet = 316 ENetPacket* enet_packet =
@@ -335,11 +347,13 @@ void Room::RoomImpl::HandleWifiPacket(const ENetEvent* event) {
335 ENET_PACKET_FLAG_RELIABLE); 347 ENET_PACKET_FLAG_RELIABLE);
336 348
337 if (destination_address == BroadcastMac) { // Send the data to everyone except the sender 349 if (destination_address == BroadcastMac) { // Send the data to everyone except the sender
350 std::lock_guard<std::mutex> lock(member_mutex);
338 for (const auto& member : members) { 351 for (const auto& member : members) {
339 if (member.peer != event->peer) 352 if (member.peer != event->peer)
340 enet_peer_send(member.peer, 0, enet_packet); 353 enet_peer_send(member.peer, 0, enet_packet);
341 } 354 }
342 } else { // Send the data only to the destination client 355 } else { // Send the data only to the destination client
356 std::lock_guard<std::mutex> lock(member_mutex);
343 auto member = std::find_if(members.begin(), members.end(), 357 auto member = std::find_if(members.begin(), members.end(),
344 [destination_address](const Member& member) -> bool { 358 [destination_address](const Member& member) -> bool {
345 return member.mac_address == destination_address; 359 return member.mac_address == destination_address;
@@ -361,6 +375,8 @@ void Room::RoomImpl::HandleChatPacket(const ENetEvent* event) {
361 auto CompareNetworkAddress = [event](const Member member) -> bool { 375 auto CompareNetworkAddress = [event](const Member member) -> bool {
362 return member.peer == event->peer; 376 return member.peer == event->peer;
363 }; 377 };
378
379 std::lock_guard<std::mutex> lock(member_mutex);
364 const auto sending_member = std::find_if(members.begin(), members.end(), CompareNetworkAddress); 380 const auto sending_member = std::find_if(members.begin(), members.end(), CompareNetworkAddress);
365 if (sending_member == members.end()) { 381 if (sending_member == members.end()) {
366 return; // Received a chat message from a unknown sender 382 return; // Received a chat message from a unknown sender
@@ -385,22 +401,32 @@ void Room::RoomImpl::HandleGameNamePacket(const ENetEvent* event) {
385 in_packet.Append(event->packet->data, event->packet->dataLength); 401 in_packet.Append(event->packet->data, event->packet->dataLength);
386 402
387 in_packet.IgnoreBytes(sizeof(u8)); // Igonore the message type 403 in_packet.IgnoreBytes(sizeof(u8)); // Igonore the message type
388 std::string game_name; 404 GameInfo game_info;
389 in_packet >> game_name; 405 in_packet >> game_info.name;
390 auto member = 406 in_packet >> game_info.id;
391 std::find_if(members.begin(), members.end(), 407
392 [event](const Member& member) -> bool { return member.peer == event->peer; }); 408 {
393 if (member != members.end()) { 409 std::lock_guard<std::mutex> lock(member_mutex);
394 member->game_name = game_name; 410 auto member =
395 BroadcastRoomInformation(); 411 std::find_if(members.begin(), members.end(), [event](const Member& member) -> bool {
412 return member.peer == event->peer;
413 });
414 if (member != members.end()) {
415 member->game_info = game_info;
416 }
396 } 417 }
418 BroadcastRoomInformation();
397} 419}
398 420
399void Room::RoomImpl::HandleClientDisconnection(ENetPeer* client) { 421void Room::RoomImpl::HandleClientDisconnection(ENetPeer* client) {
400 // Remove the client from the members list. 422 // Remove the client from the members list.
401 members.erase(std::remove_if(members.begin(), members.end(), 423 {
402 [client](const Member& member) { return member.peer == client; }), 424 std::lock_guard<std::mutex> lock(member_mutex);
403 members.end()); 425 members.erase(
426 std::remove_if(members.begin(), members.end(),
427 [client](const Member& member) { return member.peer == client; }),
428 members.end());
429 }
404 430
405 // Announce the change to all clients. 431 // Announce the change to all clients.
406 enet_peer_disconnect(client, 0); 432 enet_peer_disconnect(client, 0);
@@ -437,6 +463,19 @@ const RoomInformation& Room::GetRoomInformation() const {
437 return room_impl->room_information; 463 return room_impl->room_information;
438} 464}
439 465
466std::vector<Room::Member> Room::GetRoomMemberList() const {
467 std::vector<Room::Member> member_list;
468 std::lock_guard<std::mutex> lock(room_impl->member_mutex);
469 for (const auto& member_impl : room_impl->members) {
470 Member member;
471 member.nickname = member_impl.nickname;
472 member.mac_address = member_impl.mac_address;
473 member.game_info = member_impl.game_info;
474 member_list.push_back(member);
475 }
476 return member_list;
477};
478
440void Room::Destroy() { 479void Room::Destroy() {
441 room_impl->state = State::Closed; 480 room_impl->state = State::Closed;
442 room_impl->room_thread->join(); 481 room_impl->room_thread->join();
@@ -447,7 +486,10 @@ void Room::Destroy() {
447 } 486 }
448 room_impl->room_information = {}; 487 room_impl->room_information = {};
449 room_impl->server = nullptr; 488 room_impl->server = nullptr;
450 room_impl->members.clear(); 489 {
490 std::lock_guard<std::mutex> lock(room_impl->member_mutex);
491 room_impl->members.clear();
492 }
451 room_impl->room_information.member_slots = 0; 493 room_impl->room_information.member_slots = 0;
452 room_impl->room_information.name.clear(); 494 room_impl->room_information.name.clear();
453} 495}
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 @@
7#include <array> 7#include <array>
8#include <memory> 8#include <memory>
9#include <string> 9#include <string>
10#include <vector>
10#include "common/common_types.h" 11#include "common/common_types.h"
11 12
12namespace Network { 13namespace Network {
@@ -21,6 +22,11 @@ struct RoomInformation {
21 u32 member_slots; ///< Maximum number of members in this room 22 u32 member_slots; ///< Maximum number of members in this room
22}; 23};
23 24
25struct GameInfo {
26 std::string name{""};
27 u64 id{0};
28};
29
24using MacAddress = std::array<u8, 6>; 30using MacAddress = std::array<u8, 6>;
25/// A special MAC address that tells the room we're joining to assign us a MAC address 31/// A special MAC address that tells the room we're joining to assign us a MAC address
26/// automatically. 32/// automatically.
@@ -34,7 +40,7 @@ enum RoomMessageTypes : u8 {
34 IdJoinRequest = 1, 40 IdJoinRequest = 1,
35 IdJoinSuccess, 41 IdJoinSuccess,
36 IdRoomInformation, 42 IdRoomInformation,
37 IdSetGameName, 43 IdSetGameInfo,
38 IdWifiPacket, 44 IdWifiPacket,
39 IdChatMessage, 45 IdChatMessage,
40 IdNameCollision, 46 IdNameCollision,
@@ -51,6 +57,12 @@ public:
51 Closed, ///< The room is not opened and can not accept connections. 57 Closed, ///< The room is not opened and can not accept connections.
52 }; 58 };
53 59
60 struct Member {
61 std::string nickname; ///< The nickname of the member.
62 GameInfo game_info; ///< The current game of the member
63 MacAddress mac_address; ///< The assigned mac address of the member.
64 };
65
54 Room(); 66 Room();
55 ~Room(); 67 ~Room();
56 68
@@ -65,6 +77,11 @@ public:
65 const RoomInformation& GetRoomInformation() const; 77 const RoomInformation& GetRoomInformation() const;
66 78
67 /** 79 /**
80 * Gets a list of the mbmers connected to the room.
81 */
82 std::vector<Member> GetRoomMemberList() const;
83
84 /**
68 * Creates the socket for this room. Will bind to default address if 85 * Creates the socket for this room. Will bind to default address if
69 * server is empty string. 86 * server is empty string.
70 */ 87 */
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 @@
5#include <atomic> 5#include <atomic>
6#include <list> 6#include <list>
7#include <mutex> 7#include <mutex>
8#include <set>
8#include <thread> 9#include <thread>
9#include "common/assert.h" 10#include "common/assert.h"
10#include "enet/enet.h" 11#include "enet/enet.h"
@@ -25,6 +26,9 @@ public:
25 /// Information about the room we're connected to. 26 /// Information about the room we're connected to.
26 RoomInformation room_information; 27 RoomInformation room_information;
27 28
29 /// The current game name, id and version
30 GameInfo current_game_info;
31
28 std::atomic<State> state{State::Idle}; ///< Current state of the RoomMember. 32 std::atomic<State> state{State::Idle}; ///< Current state of the RoomMember.
29 void SetState(const State new_state); 33 void SetState(const State new_state);
30 bool IsConnected() const; 34 bool IsConnected() const;
@@ -37,6 +41,24 @@ public:
37 std::unique_ptr<std::thread> loop_thread; 41 std::unique_ptr<std::thread> loop_thread;
38 std::mutex send_list_mutex; ///< Mutex that controls access to the `send_list` variable. 42 std::mutex send_list_mutex; ///< Mutex that controls access to the `send_list` variable.
39 std::list<Packet> send_list; ///< A list that stores all packets to send the async 43 std::list<Packet> send_list; ///< A list that stores all packets to send the async
44
45 template <typename T>
46 using CallbackSet = std::set<CallbackHandle<T>>;
47 std::mutex callback_mutex; ///< The mutex used for handling callbacks
48
49 class Callbacks {
50 public:
51 template <typename T>
52 CallbackSet<T>& Get();
53
54 private:
55 CallbackSet<WifiPacket> callback_set_wifi_packet;
56 CallbackSet<ChatEntry> callback_set_chat_messages;
57 CallbackSet<RoomInformation> callback_set_room_information;
58 CallbackSet<State> callback_set_state;
59 };
60 Callbacks callbacks; ///< All CallbackSets to all events
61
40 void MemberLoop(); 62 void MemberLoop();
41 63
42 void StartLoop(); 64 void StartLoop();
@@ -84,12 +106,20 @@ public:
84 * Disconnects the RoomMember from the Room 106 * Disconnects the RoomMember from the Room
85 */ 107 */
86 void Disconnect(); 108 void Disconnect();
109
110 template <typename T>
111 void Invoke(const T& data);
112
113 template <typename T>
114 CallbackHandle<T> Bind(std::function<void(const T&)> callback);
87}; 115};
88 116
89// RoomMemberImpl 117// RoomMemberImpl
90void RoomMember::RoomMemberImpl::SetState(const State new_state) { 118void RoomMember::RoomMemberImpl::SetState(const State new_state) {
91 state = new_state; 119 if (state != new_state) {
92 // TODO(B3N30): Invoke the callback functions 120 state = new_state;
121 Invoke<State>(state);
122 }
93} 123}
94 124
95bool RoomMember::RoomMemberImpl::IsConnected() const { 125bool RoomMember::RoomMemberImpl::IsConnected() const {
@@ -195,9 +225,10 @@ void RoomMember::RoomMemberImpl::HandleRoomInformationPacket(const ENetEvent* ev
195 for (auto& member : member_information) { 225 for (auto& member : member_information) {
196 packet >> member.nickname; 226 packet >> member.nickname;
197 packet >> member.mac_address; 227 packet >> member.mac_address;
198 packet >> member.game_name; 228 packet >> member.game_info.name;
229 packet >> member.game_info.id;
199 } 230 }
200 // TODO(B3N30): Invoke callbacks 231 Invoke(room_information);
201} 232}
202 233
203void RoomMember::RoomMemberImpl::HandleJoinPacket(const ENetEvent* event) { 234void RoomMember::RoomMemberImpl::HandleJoinPacket(const ENetEvent* event) {
@@ -209,7 +240,7 @@ void RoomMember::RoomMemberImpl::HandleJoinPacket(const ENetEvent* event) {
209 240
210 // Parse the MAC Address from the packet 241 // Parse the MAC Address from the packet
211 packet >> mac_address; 242 packet >> mac_address;
212 // TODO(B3N30): Invoke callbacks 243 SetState(State::Joined);
213} 244}
214 245
215void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) { 246void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) {
@@ -235,7 +266,7 @@ void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) {
235 266
236 packet >> wifi_packet.data; 267 packet >> wifi_packet.data;
237 268
238 // TODO(B3N30): Invoke callbacks 269 Invoke<WifiPacket>(wifi_packet);
239} 270}
240 271
241void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) { 272void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) {
@@ -248,7 +279,7 @@ void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) {
248 ChatEntry chat_entry{}; 279 ChatEntry chat_entry{};
249 packet >> chat_entry.nickname; 280 packet >> chat_entry.nickname;
250 packet >> chat_entry.message; 281 packet >> chat_entry.message;
251 // TODO(B3N30): Invoke callbacks 282 Invoke<ChatEntry>(chat_entry);
252} 283}
253 284
254void RoomMember::RoomMemberImpl::Disconnect() { 285void RoomMember::RoomMemberImpl::Disconnect() {
@@ -276,6 +307,46 @@ void RoomMember::RoomMemberImpl::Disconnect() {
276 server = nullptr; 307 server = nullptr;
277} 308}
278 309
310template <>
311RoomMember::RoomMemberImpl::CallbackSet<WifiPacket>& RoomMember::RoomMemberImpl::Callbacks::Get() {
312 return callback_set_wifi_packet;
313}
314
315template <>
316RoomMember::RoomMemberImpl::CallbackSet<RoomMember::State>&
317RoomMember::RoomMemberImpl::Callbacks::Get() {
318 return callback_set_state;
319}
320
321template <>
322RoomMember::RoomMemberImpl::CallbackSet<RoomInformation>&
323RoomMember::RoomMemberImpl::Callbacks::Get() {
324 return callback_set_room_information;
325}
326
327template <>
328RoomMember::RoomMemberImpl::CallbackSet<ChatEntry>& RoomMember::RoomMemberImpl::Callbacks::Get() {
329 return callback_set_chat_messages;
330}
331
332template <typename T>
333void RoomMember::RoomMemberImpl::Invoke(const T& data) {
334 std::lock_guard<std::mutex> lock(callback_mutex);
335 CallbackSet<T> callback_set = callbacks.Get<T>();
336 for (auto const& callback : callback_set)
337 (*callback)(data);
338}
339
340template <typename T>
341RoomMember::CallbackHandle<T> RoomMember::RoomMemberImpl::Bind(
342 std::function<void(const T&)> callback) {
343 std::lock_guard<std::mutex> lock(callback_mutex);
344 CallbackHandle<T> handle;
345 handle = std::make_shared<std::function<void(const T&)>>(callback);
346 callbacks.Get<T>().insert(handle);
347 return handle;
348}
349
279// RoomMember 350// RoomMember
280RoomMember::RoomMember() : room_member_impl{std::make_unique<RoomMemberImpl>()} { 351RoomMember::RoomMember() : room_member_impl{std::make_unique<RoomMemberImpl>()} {
281 room_member_impl->client = enet_host_create(nullptr, 1, NumChannels, 0, 0); 352 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
339 room_member_impl->SetState(State::Joining); 410 room_member_impl->SetState(State::Joining);
340 room_member_impl->StartLoop(); 411 room_member_impl->StartLoop();
341 room_member_impl->SendJoinRequest(nick, preferred_mac); 412 room_member_impl->SendJoinRequest(nick, preferred_mac);
413 SendGameInfo(room_member_impl->current_game_info);
342 } else { 414 } else {
343 room_member_impl->SetState(State::CouldNotConnect); 415 room_member_impl->SetState(State::CouldNotConnect);
344 } 416 }
@@ -366,17 +438,53 @@ void RoomMember::SendChatMessage(const std::string& message) {
366 room_member_impl->Send(std::move(packet)); 438 room_member_impl->Send(std::move(packet));
367} 439}
368 440
369void RoomMember::SendGameName(const std::string& game_name) { 441void RoomMember::SendGameInfo(const GameInfo& game_info) {
442 room_member_impl->current_game_info = game_info;
443 if (!IsConnected())
444 return;
445
370 Packet packet; 446 Packet packet;
371 packet << static_cast<u8>(IdSetGameName); 447 packet << static_cast<u8>(IdSetGameInfo);
372 packet << game_name; 448 packet << game_info.name;
449 packet << game_info.id;
373 room_member_impl->Send(std::move(packet)); 450 room_member_impl->Send(std::move(packet));
374} 451}
375 452
453RoomMember::CallbackHandle<RoomMember::State> RoomMember::BindOnStateChanged(
454 std::function<void(const RoomMember::State&)> callback) {
455 return room_member_impl->Bind(callback);
456}
457
458RoomMember::CallbackHandle<WifiPacket> RoomMember::BindOnWifiPacketReceived(
459 std::function<void(const WifiPacket&)> callback) {
460 return room_member_impl->Bind(callback);
461}
462
463RoomMember::CallbackHandle<RoomInformation> RoomMember::BindOnRoomInformationChanged(
464 std::function<void(const RoomInformation&)> callback) {
465 return room_member_impl->Bind(callback);
466}
467
468RoomMember::CallbackHandle<ChatEntry> RoomMember::BindOnChatMessageRecieved(
469 std::function<void(const ChatEntry&)> callback) {
470 return room_member_impl->Bind(callback);
471}
472
473template <typename T>
474void RoomMember::Unbind(CallbackHandle<T> handle) {
475 std::lock_guard<std::mutex> lock(room_member_impl->callback_mutex);
476 room_member_impl->callbacks.Get<T>().erase(handle);
477}
478
376void RoomMember::Leave() { 479void RoomMember::Leave() {
377 room_member_impl->SetState(State::Idle); 480 room_member_impl->SetState(State::Idle);
378 room_member_impl->loop_thread->join(); 481 room_member_impl->loop_thread->join();
379 room_member_impl->loop_thread.reset(); 482 room_member_impl->loop_thread.reset();
380} 483}
381 484
485template void RoomMember::Unbind(CallbackHandle<WifiPacket>);
486template void RoomMember::Unbind(CallbackHandle<RoomMember::State>);
487template void RoomMember::Unbind(CallbackHandle<RoomInformation>);
488template void RoomMember::Unbind(CallbackHandle<ChatEntry>);
489
382} // namespace Network 490} // 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 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <functional>
7#include <memory> 8#include <memory>
8#include <string> 9#include <string>
9#include <vector> 10#include <vector>
@@ -53,12 +54,23 @@ public:
53 54
54 struct MemberInformation { 55 struct MemberInformation {
55 std::string nickname; ///< Nickname of the member. 56 std::string nickname; ///< Nickname of the member.
56 std::string game_name; ///< Name of the game they're currently playing, or empty if they're 57 GameInfo game_info; ///< Name of the game they're currently playing, or empty if they're
57 /// not playing anything. 58 /// not playing anything.
58 MacAddress mac_address; ///< MAC address associated with this member. 59 MacAddress mac_address; ///< MAC address associated with this member.
59 }; 60 };
60 using MemberList = std::vector<MemberInformation>; 61 using MemberList = std::vector<MemberInformation>;
61 62
63 // The handle for the callback functions
64 template <typename T>
65 using CallbackHandle = std::shared_ptr<std::function<void(const T&)>>;
66
67 /**
68 * Unbinds a callback function from the events.
69 * @param handle The connection handle to disconnect
70 */
71 template <typename T>
72 void Unbind(CallbackHandle<T> handle);
73
62 RoomMember(); 74 RoomMember();
63 ~RoomMember(); 75 ~RoomMember();
64 76
@@ -113,10 +125,49 @@ public:
113 void SendChatMessage(const std::string& message); 125 void SendChatMessage(const std::string& message);
114 126
115 /** 127 /**
116 * Sends the current game name to the room. 128 * Sends the current game info to the room.
117 * @param game_name The game name. 129 * @param game_info The game information.
130 */
131 void SendGameInfo(const GameInfo& game_info);
132
133 /**
134 * Binds a function to an event that will be triggered every time the State of the member
135 * changed. The function wil be called every time the event is triggered. The callback function
136 * must not bind or unbind a function. Doing so will cause a deadlock
137 * @param callback The function to call
138 * @return A handle used for removing the function from the registered list
139 */
140 CallbackHandle<State> BindOnStateChanged(std::function<void(const State&)> callback);
141
142 /**
143 * Binds a function to an event that will be triggered every time a WifiPacket is received.
144 * The function wil be called everytime the event is triggered.
145 * The callback function must not bind or unbind a function. Doing so will cause a deadlock
146 * @param callback The function to call
147 * @return A handle used for removing the function from the registered list
148 */
149 CallbackHandle<WifiPacket> BindOnWifiPacketReceived(
150 std::function<void(const WifiPacket&)> callback);
151
152 /**
153 * Binds a function to an event that will be triggered every time the RoomInformation changes.
154 * The function wil be called every time the event is triggered.
155 * The callback function must not bind or unbind a function. Doing so will cause a deadlock
156 * @param callback The function to call
157 * @return A handle used for removing the function from the registered list
158 */
159 CallbackHandle<RoomInformation> BindOnRoomInformationChanged(
160 std::function<void(const RoomInformation&)> callback);
161
162 /**
163 * Binds a function to an event that will be triggered every time a ChatMessage is received.
164 * The function wil be called every time the event is triggered.
165 * The callback function must not bind or unbind a function. Doing so will cause a deadlock
166 * @param callback The function to call
167 * @return A handle used for removing the function from the registered list
118 */ 168 */
119 void SendGameName(const std::string& game_name); 169 CallbackHandle<ChatEntry> BindOnChatMessageRecieved(
170 std::function<void(const ChatEntry&)> callback);
120 171
121 /** 172 /**
122 * Leaves the current room. 173 * 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) {
525 "float geo_factor = 1.0;\n"; 525 "float geo_factor = 1.0;\n";
526 526
527 // Compute fragment normals and tangents 527 // Compute fragment normals and tangents
528 const std::string pertubation = 528 auto Perturbation = [&]() {
529 "2.0 * (" + SampleTexture(config, lighting.bump_selector) + ").rgb - 1.0"; 529 return "2.0 * (" + SampleTexture(config, lighting.bump_selector) + ").rgb - 1.0";
530 };
530 if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) { 531 if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) {
531 // Bump mapping is enabled using a normal map 532 // Bump mapping is enabled using a normal map
532 out += "vec3 surface_normal = " + pertubation + ";\n"; 533 out += "vec3 surface_normal = " + Perturbation() + ";\n";
533 534
534 // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher 535 // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher
535 // precision result 536 // precision result
@@ -543,7 +544,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
543 out += "vec3 surface_tangent = vec3(1.0, 0.0, 0.0);\n"; 544 out += "vec3 surface_tangent = vec3(1.0, 0.0, 0.0);\n";
544 } else if (lighting.bump_mode == LightingRegs::LightingBumpMode::TangentMap) { 545 } else if (lighting.bump_mode == LightingRegs::LightingBumpMode::TangentMap) {
545 // Bump mapping is enabled using a tangent map 546 // Bump mapping is enabled using a tangent map
546 out += "vec3 surface_tangent = " + pertubation + ";\n"; 547 out += "vec3 surface_tangent = " + Perturbation() + ";\n";
547 // Mathematically, recomputing Z-component of the tangent vector won't affect the relevant 548 // Mathematically, recomputing Z-component of the tangent vector won't affect the relevant
548 // computation below, which is also confirmed on 3DS. So we don't bother recomputing here 549 // computation below, which is also confirmed on 3DS. So we don't bother recomputing here
549 // even if 'renorm' is enabled. 550 // even if 'renorm' is enabled.
diff --git a/src/video_core/swrasterizer/lighting.cpp b/src/video_core/swrasterizer/lighting.cpp
index d61e6d572..ffd35792a 100644
--- a/src/video_core/swrasterizer/lighting.cpp
+++ b/src/video_core/swrasterizer/lighting.cpp
@@ -95,6 +95,12 @@ std::tuple<Math::Vec4<u8>, Math::Vec4<u8>> ComputeFragmentsColors(
95 result = Math::Dot(light_vector, normal); 95 result = Math::Dot(light_vector, normal);
96 break; 96 break;
97 97
98 case LightingRegs::LightingLutInput::SP: {
99 Math::Vec3<s32> spot_dir{light_config.spot_x.Value(), light_config.spot_y.Value(),
100 light_config.spot_z.Value()};
101 result = Math::Dot(light_vector, spot_dir.Cast<float>() / 2047.0f);
102 break;
103 }
98 default: 104 default:
99 LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %u\n", static_cast<u32>(input)); 105 LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %u\n", static_cast<u32>(input));
100 UNIMPLEMENTED(); 106 UNIMPLEMENTED();
@@ -125,6 +131,16 @@ std::tuple<Math::Vec4<u8>, Math::Vec4<u8>> ComputeFragmentsColors(
125 LookupLightingLut(lighting_state, static_cast<size_t>(sampler), index, delta); 131 LookupLightingLut(lighting_state, static_cast<size_t>(sampler), index, delta);
126 }; 132 };
127 133
134 // If enabled, compute spot light attenuation value
135 float spot_atten = 1.0f;
136 if (!lighting.IsSpotAttenDisabled(num) &&
137 LightingRegs::IsLightingSamplerSupported(
138 lighting.config0.config, LightingRegs::LightingSampler::SpotlightAttenuation)) {
139 auto lut = LightingRegs::SpotlightAttenuationSampler(num);
140 spot_atten = GetLutValue(lighting.lut_input.sp, lighting.abs_lut_input.disable_sp == 0,
141 lighting.lut_scale.sp, lut);
142 }
143
128 // Specular 0 component 144 // Specular 0 component
129 float d0_lut_value = 1.0f; 145 float d0_lut_value = 1.0f;
130 if (lighting.config1.disable_lut_d0 == 0 && 146 if (lighting.config1.disable_lut_d0 == 0 &&
@@ -226,10 +242,10 @@ std::tuple<Math::Vec4<u8>, Math::Vec4<u8>> ComputeFragmentsColors(
226 242
227 auto diffuse = 243 auto diffuse =
228 light_config.diffuse.ToVec3f() * dot_product + light_config.ambient.ToVec3f(); 244 light_config.diffuse.ToVec3f() * dot_product + light_config.ambient.ToVec3f();
229 diffuse_sum += Math::MakeVec(diffuse * dist_atten, 0.0f); 245 diffuse_sum += Math::MakeVec(diffuse * dist_atten * spot_atten, 0.0f);
230 246
231 specular_sum += 247 specular_sum += Math::MakeVec(
232 Math::MakeVec((specular_0 + specular_1) * clamp_highlights * dist_atten, 0.0f); 248 (specular_0 + specular_1) * clamp_highlights * dist_atten * spot_atten, 0.0f);
233 } 249 }
234 250
235 diffuse_sum += Math::MakeVec(lighting.global_ambient.ToVec3f(), 0.0f); 251 diffuse_sum += Math::MakeVec(lighting.global_ambient.ToVec3f(), 0.0f);