summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis-build-docker.sh20
-rwxr-xr-x.travis-build.sh10
-rwxr-xr-x.travis-deps.sh30
-rwxr-xr-x.travis-upload.sh11
-rw-r--r--.travis.yml16
-rw-r--r--appveyor.yml20
-rw-r--r--src/citra/default_ini.h6
-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/input_common/sdl/sdl.cpp2
-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
22 files changed, 573 insertions, 142 deletions
diff --git a/.travis-build-docker.sh b/.travis-build-docker.sh
new file mode 100644
index 000000000..ca6fae42b
--- /dev/null
+++ b/.travis-build-docker.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+set -e
+set -x
+
+cd /citra
+
+apt-get update
+apt-get install -y build-essential libsdl2-dev qtbase5-dev libqt5opengl5-dev libcurl4-openssl-dev libssl-dev wget git
+
+# Get a recent version of CMake
+wget https://cmake.org/files/v3.9/cmake-3.9.0-Linux-x86_64.sh
+echo y | sh cmake-3.9.0-Linux-x86_64.sh --prefix=cmake
+export PATH=/citra/cmake/cmake-3.9.0-Linux-x86_64/bin:$PATH
+
+mkdir build && cd build
+cmake .. -DCMAKE_BUILD_TYPE=Release
+make -j4
+
+ctest -VV -C Release
diff --git a/.travis-build.sh b/.travis-build.sh
index df6e236b6..64f5aed94 100755
--- a/.travis-build.sh
+++ b/.travis-build.sh
@@ -44,15 +44,7 @@ fi
#if OS is linux or is not set
if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
- export CC=gcc-6
- export CXX=g++-6
- export PKG_CONFIG_PATH=$HOME/.local/lib/pkgconfig:$PKG_CONFIG_PATH
-
- mkdir build && cd build
- cmake ..
- make -j4
-
- ctest -VV -C Release
+ docker run -v $(pwd):/citra ubuntu:16.04 /bin/bash /citra/.travis-build-docker.sh
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
set -o pipefail
diff --git a/.travis-deps.sh b/.travis-deps.sh
index 25a287c7f..0cee68041 100755
--- a/.travis-deps.sh
+++ b/.travis-deps.sh
@@ -5,35 +5,7 @@ set -x
#if OS is linux or is not set
if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
- export CC=gcc-6
- export CXX=g++-6
- mkdir -p $HOME/.local
-
- if [ ! -e $HOME/.local/bin/cmake ]; then
- echo "CMake not found in the cache, get and extract it..."
- curl -L http://www.cmake.org/files/v3.6/cmake-3.6.3-Linux-x86_64.tar.gz \
- | tar -xz -C $HOME/.local --strip-components=1
- else
- echo "Using cached CMake"
- fi
-
- if [ ! -e $HOME/.local/lib/libSDL2.la ]; then
- echo "SDL2 not found in cache, get and build it..."
- wget http://libsdl.org/release/SDL2-2.0.5.tar.gz -O - | tar xz
- cd SDL2-2.0.5
- ./configure --prefix=$HOME/.local
- make -j4 && make install
- else
- echo "Using cached SDL2"
- fi
-
- export DEBIAN_FRONTEND=noninteractive
- # Amazing placebo security
- curl http://apt.llvm.org/llvm-snapshot.gpg.key | sudo -E apt-key add -
- sudo -E add-apt-repository "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.9 main"
- sudo -E apt-get -yq update
- sudo -E apt-get -yq install clang-format-3.9
-
+ docker pull ubuntu:16.04
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
brew update
brew install qt5 sdl2 dylibbundler p7zip
diff --git a/.travis-upload.sh b/.travis-upload.sh
index 17959b0e1..8c1fa21c5 100755
--- a/.travis-upload.sh
+++ b/.travis-upload.sh
@@ -123,9 +123,16 @@ cp README.md "$REV_NAME"
tar $COMPRESSION_FLAGS "$ARCHIVE_NAME" "$REV_NAME"
-mv "$REV_NAME" nightly
+# Find out what release we are building
+if [ -z $TRAVIS_TAG ]; then
+ RELEASE_NAME=head
+else
+ RELEASE_NAME=$(echo $TRAVIS_TAG | cut -d- -f1)
+fi
+
+mv "$REV_NAME" $RELEASE_NAME
-7z a "$REV_NAME.7z" nightly
+7z a "$REV_NAME.7z" $RELEASE_NAME
# move the compiled archive into the artifacts directory to be uploaded by travis releases
mv "$ARCHIVE_NAME" artifacts/
diff --git a/.travis.yml b/.travis.yml
index 3da22eb5d..b92d7f236 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,23 +8,15 @@ matrix:
sudo: false
osx_image: xcode7.3
+services:
+ - docker
+
addons:
apt:
- sources:
- - ubuntu-toolchain-r-test
packages:
- - gcc-6
- - g++-6
- - qt5-default
- - libqt5opengl5-dev
- - xorg-dev
- - lib32stdc++6 # For CMake
+ - clang-format-3.9
- p7zip-full
-cache:
- directories:
- - "$HOME/.local"
-
install: "./.travis-deps.sh"
script: "./.travis-build.sh"
after_success: "./.travis-upload.sh"
diff --git a/appveyor.yml b/appveyor.yml
index eb4e7df87..94e9969f5 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -46,13 +46,21 @@ after_build:
7z a -tzip $MSVC_BUILD_PDB .\build\bin\release\*.pdb
rm .\build\bin\release\*.pdb
- mkdir nightly
- Copy-Item .\build\bin\release\* -Destination nightly -Recurse
- Copy-Item .\license.txt -Destination nightly
- Copy-Item .\README.md -Destination nightly
+ # Find out which kind of release we are producing by tag name
+ if ($env:APPVEYOR_REPO_TAG_NAME) {
+ $RELEASE_DIST, $RELEASE_VERSION = $env:APPVEYOR_REPO_TAG_NAME.split('-')
+ } else {
+ # There is no repo tag - make assumptions
+ $RELEASE_DIST = "head"
+ }
- 7z a -tzip $MSVC_BUILD_NAME nightly\*
- 7z a $MSVC_SEVENZIP nightly
+ mkdir $RELEASE_DIST
+ Copy-Item .\build\bin\release\* -Destination $RELEASE_DIST -Recurse
+ Copy-Item .\license.txt -Destination $RELEASE_DIST
+ Copy-Item .\README.md -Destination $RELEASE_DIST
+
+ 7z a -tzip $MSVC_BUILD_NAME $RELEASE_DIST\*
+ 7z a $MSVC_SEVENZIP $RELEASE_DIST
test_script:
- cd build && ctest -VV -C Release && cd ..
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.