From 309fcc4142ec030c2fd16378ef090efe0d596304 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sat, 8 Jul 2023 15:23:27 -0500 Subject: [PATCH] Convert rumble_queue into a generic feedback_queue for gamepad messages --- src/input.cpp | 16 ++-- src/main.h | 2 +- src/platform/common.h | 82 ++++++++++++++--- src/platform/linux/input.cpp | 22 ++--- src/platform/macos/input.cpp | 4 +- src/platform/windows/input.cpp | 70 +++++++++++--- src/stream.cpp | 164 ++++++++++++++++++++++++++------- 7 files changed, 278 insertions(+), 82 deletions(-) diff --git a/src/input.cpp b/src/input.cpp index c7b0be14..b404516b 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -140,12 +140,12 @@ namespace input { input_t( safe::mail_raw_t::event_t touch_port_event, - platf::rumble_queue_t rumble_queue): + platf::feedback_queue_t feedback_queue): shortcutFlags {}, active_gamepad_state {}, gamepads(MAX_GAMEPADS), touch_port_event { std::move(touch_port_event) }, - rumble_queue { std::move(rumble_queue) }, + feedback_queue { std::move(feedback_queue) }, mouse_left_button_timeout {}, touch_port { { 0, 0, 0, 0 }, 0, 0, 1.0f } {} @@ -156,7 +156,7 @@ namespace input { std::vector gamepads; safe::mail_raw_t::event_t touch_port_event; - platf::rumble_queue_t rumble_queue; + platf::feedback_queue_t feedback_queue; std::list> input_queue; std::mutex input_queue_lock; @@ -703,7 +703,7 @@ namespace input { } int - updateGamepads(std::vector &gamepads, std::int16_t old_state, std::int16_t new_state, const platf::rumble_queue_t &rumble_queue) { + updateGamepads(std::vector &gamepads, std::int16_t old_state, std::int16_t new_state, const platf::feedback_queue_t &feedback_queue) { auto xorGamepadMask = old_state ^ new_state; if (!xorGamepadMask) { return 0; @@ -729,7 +729,7 @@ namespace input { return -1; } - if (platf::alloc_gamepad(platf_input, id, {}, rumble_queue)) { + if (platf::alloc_gamepad(platf_input, id, {}, feedback_queue)) { free_id(gamepadMask, id); // allocating a gamepad failed: solution: ignore gamepads // The implementations of platf::alloc_gamepad already has logging @@ -776,7 +776,7 @@ namespace input { input->active_gamepad_state |= (1 << packet->controllerNumber); // Allocate a new gamepad - if (platf::alloc_gamepad(platf_input, packet->controllerNumber, arrival, input->rumble_queue)) { + if (platf::alloc_gamepad(platf_input, packet->controllerNumber, arrival, input->feedback_queue)) { free_id(gamepadMask, packet->controllerNumber); return; } @@ -900,7 +900,7 @@ namespace input { return; } - if (updateGamepads(input->gamepads, input->active_gamepad_state, packet->activeGamepadMask, input->rumble_queue)) { + if (updateGamepads(input->gamepads, input->active_gamepad_state, packet->activeGamepadMask, input->feedback_queue)) { return; } @@ -1443,7 +1443,7 @@ namespace input { alloc(safe::mail_t mail) { auto input = std::make_shared( mail->event(mail::touch_port), - mail->queue(mail::rumble)); + mail->queue(mail::gamepad_feedback)); // Workaround to ensure new frames will be captured when a client connects task_pool.pushDelayed([]() { diff --git a/src/main.h b/src/main.h index e98206f3..d5a6eeb5 100644 --- a/src/main.h +++ b/src/main.h @@ -62,7 +62,7 @@ namespace mail { // Local mail MAIL(touch_port); MAIL(idr); - MAIL(rumble); + MAIL(gamepad_feedback); MAIL(hdr); #undef MAIL diff --git a/src/platform/common.h b/src/platform/common.h index 49e1b1c1..eb1b45fd 100644 --- a/src/platform/common.h +++ b/src/platform/common.h @@ -72,17 +72,75 @@ namespace platf { constexpr std::uint32_t TOUCHPAD_BUTTON = 0x100000; constexpr std::uint32_t MISC_BUTTON = 0x200000; - struct rumble_t { - KITTY_DEFAULT_CONSTR(rumble_t) - - rumble_t(std::uint16_t id, std::uint16_t lowfreq, std::uint16_t highfreq): - id { id }, lowfreq { lowfreq }, highfreq { highfreq } {} - - std::uint16_t id; - std::uint16_t lowfreq; - std::uint16_t highfreq; + enum class gamepad_feedback_e { + rumble, + rumble_triggers, + set_motion_event_state, + set_rgb_led, }; - using rumble_queue_t = safe::mail_raw_t::queue_t; + + struct gamepad_feedback_msg_t { + static gamepad_feedback_msg_t + make_rumble(std::uint16_t id, std::uint16_t lowfreq, std::uint16_t highfreq) { + gamepad_feedback_msg_t msg; + msg.type = gamepad_feedback_e::rumble; + msg.id = id; + msg.data.rumble = { lowfreq, highfreq }; + return msg; + } + + static gamepad_feedback_msg_t + make_rumble_triggers(std::uint16_t id, std::uint16_t left, std::uint16_t right) { + gamepad_feedback_msg_t msg; + msg.type = gamepad_feedback_e::rumble_triggers; + msg.id = id; + msg.data.rumble_triggers = { left, right }; + return msg; + } + + static gamepad_feedback_msg_t + make_motion_event_state(std::uint16_t id, std::uint8_t motion_type, std::uint16_t report_rate) { + gamepad_feedback_msg_t msg; + msg.type = gamepad_feedback_e::set_motion_event_state; + msg.id = id; + msg.data.motion_event_state.motion_type = motion_type; + msg.data.motion_event_state.report_rate = report_rate; + return msg; + } + + static gamepad_feedback_msg_t + make_rgb_led(std::uint16_t id, std::uint8_t r, std::uint8_t g, std::uint8_t b) { + gamepad_feedback_msg_t msg; + msg.type = gamepad_feedback_e::set_rgb_led; + msg.id = id; + msg.data.rgb_led = { r, g, b }; + return msg; + } + + gamepad_feedback_e type; + std::uint16_t id; + union { + struct { + std::uint16_t lowfreq; + std::uint16_t highfreq; + } rumble; + struct { + std::uint16_t left_trigger; + std::uint16_t right_trigger; + } rumble_triggers; + struct { + std::uint16_t report_rate; + std::uint8_t motion_type; + } motion_event_state; + struct { + std::uint8_t r; + std::uint8_t g; + std::uint8_t b; + } rgb_led; + } data; + }; + + using feedback_queue_t = safe::mail_raw_t::queue_t; namespace speaker { enum speaker_e { @@ -525,11 +583,11 @@ namespace platf { * @param input The input context. * @param nr The assigned controller number. * @param metadata Controller metadata from client (empty if none provided). - * @param rumble_queue The queue for posting rumble messages to the client. + * @param feedback_queue The queue for posting messages back to the client. * @return 0 on success. */ int - alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, rumble_queue_t rumble_queue); + alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue); void free_gamepad(input_t &input, int nr); diff --git a/src/platform/linux/input.cpp b/src/platform/linux/input.cpp index 62a2e906..c427013f 100644 --- a/src/platform/linux/input.cpp +++ b/src/platform/linux/input.cpp @@ -133,7 +133,7 @@ namespace platf { } }); - using mail_evdev_t = std::tuple; + using mail_evdev_t = std::tuple; struct keycode_t { std::uint32_t keycode; @@ -452,7 +452,7 @@ namespace platf { public: KITTY_DEFAULT_CONSTR_MOVE(effect_t) - effect_t(int gamepadnr, uinput_t::pointer dev, rumble_queue_t &&q): + effect_t(int gamepadnr, uinput_t::pointer dev, feedback_queue_t &&q): gamepadnr { gamepadnr }, dev { dev }, rumble_queue { std::move(q) }, gain { 0xFFFF }, id_to_data {} {} class data_t { @@ -634,7 +634,7 @@ namespace platf { // Used as ID for adding/removinf devices from evdev notifications uinput_t::pointer dev; - rumble_queue_t rumble_queue; + feedback_queue_t rumble_queue; int gain; @@ -774,11 +774,11 @@ namespace platf { * @brief Creates a new virtual gamepad. * @param nr The assigned controller number. * @param metadata Controller metadata from client (empty if none provided). - * @param rumble_queue The queue for posting rumble messages to the client. + * @param feedback_queue The queue for posting messages back to the client. * @return 0 on success. */ int - alloc_gamepad(int nr, const gamepad_arrival_t &metadata, rumble_queue_t &&rumble_queue) { + alloc_gamepad(int nr, const gamepad_arrival_t &metadata, feedback_queue_t &&feedback_queue) { TUPLE_2D_REF(input, gamepad_state, gamepads[nr]); int err = libevdev_uinput_create_from_device(gamepad_dev.get(), LIBEVDEV_UINPUT_OPEN_MANAGED, &input); @@ -803,7 +803,7 @@ namespace platf { rumble_ctx->rumble_queue_queue.raise( nr, input.get(), - std::move(rumble_queue), + std::move(feedback_queue), pollfd_t { dup(libevdev_uinput_get_fd(input.get())), (std::int16_t) POLLIN, @@ -1048,9 +1048,9 @@ namespace platf { TUPLE_2D(weak, strong, effect.rumble(now)); if (old_weak != weak || old_strong != strong) { - BOOST_LOG(debug) << "Sending haptic feedback: lowfreq [0x"sv << util::hex(weak).to_string_view() << "]: highfreq [0x"sv << util::hex(strong).to_string_view() << ']'; + BOOST_LOG(debug) << "Sending haptic feedback: lowfreq [0x"sv << util::hex(strong).to_string_view() << "]: highfreq [0x"sv << util::hex(weak).to_string_view() << ']'; - effect.rumble_queue->raise(effect.gamepadnr, weak, strong); + effect.rumble_queue->raise(gamepad_feedback_msg_t::make_rumble(effect.gamepadnr, strong, weak)); } } } @@ -1492,12 +1492,12 @@ namespace platf { * @param input The input context. * @param nr The assigned controller number. * @param metadata Controller metadata from client (empty if none provided). - * @param rumble_queue The queue for posting rumble messages to the client. + * @param feedback_queue The queue for posting messages back to the client. * @return 0 on success. */ int - alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, rumble_queue_t rumble_queue) { - return ((input_raw_t *) input.get())->alloc_gamepad(nr, metadata, std::move(rumble_queue)); + alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) { + return ((input_raw_t *) input.get())->alloc_gamepad(nr, metadata, std::move(feedback_queue)); } void diff --git a/src/platform/macos/input.cpp b/src/platform/macos/input.cpp index b9eb073a..17073479 100644 --- a/src/platform/macos/input.cpp +++ b/src/platform/macos/input.cpp @@ -293,11 +293,11 @@ const KeyCodeMap kKeyCodesMap[] = { * @param input The input context. * @param nr The assigned controller number. * @param metadata Controller metadata from client (empty if none provided). - * @param rumble_queue The queue for posting rumble messages to the client. + * @param feedback_queue The queue for posting messages back to the client. * @return 0 on success. */ int - alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, rumble_queue_t rumble_queue) { + alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) { BOOST_LOG(info) << "alloc_gamepad: Gamepad not yet implemented for MacOS."sv; return -1; } diff --git a/src/platform/windows/input.cpp b/src/platform/windows/input.cpp index 76f2cd74..74abfc42 100644 --- a/src/platform/windows/input.cpp +++ b/src/platform/windows/input.cpp @@ -44,12 +44,15 @@ namespace platf { struct gamepad_context_t { target_t gp; - rumble_queue_t rumble_queue; + feedback_queue_t feedback_queue; union { XUSB_REPORT x360; DS4_REPORT_EX ds4; } report; + + gamepad_feedback_msg_t last_rumble; + gamepad_feedback_msg_t last_rgb_led; }; constexpr float EARTH_G = 9.80665f; @@ -181,12 +184,12 @@ namespace platf { /** * @brief Attaches a new gamepad. * @param nr The gamepad index. - * @param rumble_queue The queue to publish rumble packets. + * @param feedback_queue The queue for posting messages back to the client. * @param gp_type The type of gamepad. * @return 0 on success. */ int - alloc_gamepad_internal(int nr, rumble_queue_t &rumble_queue, VIGEM_TARGET_TYPE gp_type) { + alloc_gamepad_internal(int nr, feedback_queue_t &feedback_queue, VIGEM_TARGET_TYPE gp_type) { auto &gamepad = gamepads[nr]; assert(!gamepad.gp); @@ -212,7 +215,7 @@ namespace platf { return -1; } - gamepad.rumble_queue = std::move(rumble_queue); + gamepad.feedback_queue = std::move(feedback_queue); if (gp_type == Xbox360Wired) { status = vigem_target_x360_register_notification(client.get(), gamepad.gp.get(), x360_notify, this); @@ -247,19 +250,52 @@ namespace platf { } /** - * @brief Pass rumble data back to the host. + * @brief Pass rumble data back to the client. * @param target The gamepad. - * @param smallMotor The small motor. * @param largeMotor The large motor. + * @param smallMotor The small motor. */ void - rumble(target_t::pointer target, std::uint8_t smallMotor, std::uint8_t largeMotor) { + rumble(target_t::pointer target, std::uint8_t largeMotor, std::uint8_t smallMotor) { for (int x = 0; x < gamepads.size(); ++x) { auto &gamepad = gamepads[x]; if (gamepad.gp.get() == target) { - gamepad.rumble_queue->raise(x, ((std::uint16_t) smallMotor) << 8, ((std::uint16_t) largeMotor) << 8); + // Convert from 8-bit to 16-bit values + uint16_t normalizedLargeMotor = largeMotor << 8; + uint16_t normalizedSmallMotor = smallMotor << 8; + // Don't resend duplicate rumble data + if (normalizedSmallMotor != gamepad.last_rumble.data.rumble.highfreq || + normalizedLargeMotor != gamepad.last_rumble.data.rumble.lowfreq) { + gamepad_feedback_msg_t msg = gamepad_feedback_msg_t::make_rumble(x, normalizedLargeMotor, normalizedSmallMotor); + gamepad.feedback_queue->raise(msg); + gamepad.last_rumble = msg; + } + return; + } + } + } + + /** + * @brief Pass RGB LED data back to the client. + * @param target The gamepad. + * @param r The red channel. + * @param g The red channel. + * @param b The red channel. + */ + void + set_rgb_led(target_t::pointer target, std::uint8_t r, std::uint8_t g, std::uint8_t b) { + for (int x = 0; x < gamepads.size(); ++x) { + auto &gamepad = gamepads[x]; + + if (gamepad.gp.get() == target) { + // Don't resend duplicate RGB data + if (r != gamepad.last_rgb_led.data.rgb_led.r || g != gamepad.last_rgb_led.data.rgb_led.g || b != gamepad.last_rgb_led.data.rgb_led.b) { + gamepad_feedback_msg_t msg = gamepad_feedback_msg_t::make_rgb_led(x, r, g, b); + gamepad.feedback_queue->raise(msg); + gamepad.last_rgb_led = msg; + } return; } } @@ -299,7 +335,7 @@ namespace platf { << "largeMotor: "sv << (int) largeMotor << std::endl << "smallMotor: "sv << (int) smallMotor; - task_pool.push(&vigem_t::rumble, (vigem_t *) userdata, target, smallMotor, largeMotor); + task_pool.push(&vigem_t::rumble, (vigem_t *) userdata, target, largeMotor, smallMotor); } void CALLBACK @@ -307,13 +343,17 @@ namespace platf { client_t::pointer client, target_t::pointer target, std::uint8_t largeMotor, std::uint8_t smallMotor, - DS4_LIGHTBAR_COLOR /* led_color */, + DS4_LIGHTBAR_COLOR led_color, void *userdata) { BOOST_LOG(debug) << "largeMotor: "sv << (int) largeMotor << std::endl - << "smallMotor: "sv << (int) smallMotor; + << "smallMotor: "sv << (int) smallMotor << std::endl + << "LED: "sv << util::hex(led_color.Red).to_string_view() << ' ' + << util::hex(led_color.Green).to_string_view() << ' ' + << util::hex(led_color.Blue).to_string_view() << std::endl; - task_pool.push(&vigem_t::rumble, (vigem_t *) userdata, target, smallMotor, largeMotor); + task_pool.push(&vigem_t::rumble, (vigem_t *) userdata, target, largeMotor, smallMotor); + task_pool.push(&vigem_t::set_rgb_led, (vigem_t *) userdata, target, led_color.Red, led_color.Green, led_color.Blue); } struct input_raw_t { @@ -554,11 +594,11 @@ namespace platf { * @param input The input context. * @param nr The assigned controller number. * @param metadata Controller metadata from client (empty if none provided). - * @param rumble_queue The queue for posting rumble messages to the client. + * @param feedback_queue The queue for posting messages back to the client. * @return 0 on success. */ int - alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, rumble_queue_t rumble_queue) { + alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) { auto raw = (input_raw_t *) input.get(); if (!raw->vigem) { @@ -596,7 +636,7 @@ namespace platf { selectedGamepadType = Xbox360Wired; } - return raw->vigem->alloc_gamepad_internal(nr, rumble_queue, selectedGamepadType); + return raw->vigem->alloc_gamepad_internal(nr, feedback_queue, selectedGamepadType); } void diff --git a/src/stream.cpp b/src/stream.cpp index 236797c5..1643dade 100644 --- a/src/stream.cpp +++ b/src/stream.cpp @@ -39,6 +39,9 @@ extern "C" { #define IDX_REQUEST_IDR_FRAME 9 #define IDX_ENCRYPTED 10 #define IDX_HDR_MODE 11 +#define IDX_RUMBLE_TRIGGER_DATA 12 +#define IDX_SET_MOTION_EVENT 13 +#define IDX_SET_RGB_LED 14 static const short packetTypes[] = { 0x0305, // Start A @@ -53,6 +56,9 @@ static const short packetTypes[] = { 0x0302, // IDR frame 0x0001, // fully encrypted 0x010e, // HDR mode + 0x5500, // Rumble triggers (Sunshine protocol extension) + 0x5501, // Set motion event (Sunshine protocol extension) + 0x5502, // Set RGB LED (Sunshine protocol extension) }; namespace asio = boost::asio; @@ -146,6 +152,31 @@ namespace stream { std::uint16_t highfreq; }; + struct control_rumble_triggers_t { + control_header_v2 header; + + std::uint16_t id; + std::uint16_t left; + std::uint16_t right; + }; + + struct control_set_motion_event_t { + control_header_v2 header; + + std::uint16_t id; + std::uint16_t reportrate; + std::uint8_t type; + }; + + struct control_set_rgb_led_t { + control_header_v2 header; + + std::uint16_t id; + std::uint8_t r; + std::uint8_t g; + std::uint8_t b; + }; + struct control_hdr_mode_t { control_header_v2 header; @@ -350,7 +381,7 @@ namespace stream { net::peer_t peer; std::uint8_t seq; - platf::rumble_queue_t rumble_queue; + platf::feedback_queue_t feedback_queue; safe::mail_raw_t::event_t hdr_queue; } control; @@ -621,37 +652,106 @@ namespace stream { return replaced; } + /** + * @brief Pass gamepad feedback data back to the client. + * @param session The session object. + * @param msg The message to pass. + * @return 0 on success. + */ int - send_rumble(session_t *session, std::uint16_t id, std::uint16_t lowfreq, std::uint16_t highfreq) { + send_feedback_msg(session_t *session, platf::gamepad_feedback_msg_t &msg) { if (!session->control.peer) { - BOOST_LOG(warning) << "Couldn't send rumble data, still waiting for PING from Moonlight"sv; + BOOST_LOG(warning) << "Couldn't send gamepad feedback data, still waiting for PING from Moonlight"sv; // Still waiting for PING from Moonlight return -1; } - control_rumble_t plaintext; - plaintext.header.type = packetTypes[IDX_RUMBLE_DATA]; - plaintext.header.payloadLength = sizeof(control_rumble_t) - sizeof(control_header_v2); + std::string payload; + if (msg.type == platf::gamepad_feedback_e::rumble) { + control_rumble_t plaintext; + plaintext.header.type = packetTypes[IDX_RUMBLE_DATA]; + plaintext.header.payloadLength = sizeof(plaintext) - sizeof(control_header_v2); - plaintext.useless = 0xC0FFEE; - plaintext.id = util::endian::little(id); - plaintext.lowfreq = util::endian::little(lowfreq); - plaintext.highfreq = util::endian::little(highfreq); + auto &data = msg.data.rumble; - BOOST_LOG(verbose) << id << " :: "sv << util::hex(lowfreq).to_string_view() << " :: "sv << util::hex(highfreq).to_string_view(); - std::array - encrypted_payload; + plaintext.useless = 0xC0FFEE; + plaintext.id = util::endian::little(msg.id); + plaintext.lowfreq = util::endian::little(data.lowfreq); + plaintext.highfreq = util::endian::little(data.highfreq); - auto payload = encode_control(session, util::view(plaintext), encrypted_payload); - if (session->broadcast_ref->control_server.send(payload, session->control.peer)) { - TUPLE_2D(port, addr, platf::from_sockaddr_ex((sockaddr *) &session->control.peer->address.address)); - BOOST_LOG(warning) << "Couldn't send termination code to ["sv << addr << ':' << port << ']'; + BOOST_LOG(verbose) << "Rumble: "sv << msg.id << " :: "sv << util::hex(data.lowfreq).to_string_view() << " :: "sv << util::hex(data.highfreq).to_string_view(); + std::array + encrypted_payload; + payload = encode_control(session, util::view(plaintext), encrypted_payload); + } + else if (msg.type == platf::gamepad_feedback_e::rumble_triggers) { + control_rumble_triggers_t plaintext; + plaintext.header.type = packetTypes[IDX_RUMBLE_TRIGGER_DATA]; + plaintext.header.payloadLength = sizeof(plaintext) - sizeof(control_header_v2); + + auto &data = msg.data.rumble_triggers; + + plaintext.id = util::endian::little(msg.id); + plaintext.left = util::endian::little(data.left_trigger); + plaintext.right = util::endian::little(data.right_trigger); + + BOOST_LOG(verbose) << "Rumble triggers: "sv << msg.id << " :: "sv << util::hex(data.left_trigger).to_string_view() << " :: "sv << util::hex(data.right_trigger).to_string_view(); + std::array + encrypted_payload; + + payload = encode_control(session, util::view(plaintext), encrypted_payload); + } + else if (msg.type == platf::gamepad_feedback_e::set_motion_event_state) { + control_set_motion_event_t plaintext; + plaintext.header.type = packetTypes[IDX_SET_MOTION_EVENT]; + plaintext.header.payloadLength = sizeof(plaintext) - sizeof(control_header_v2); + + auto &data = msg.data.motion_event_state; + + plaintext.id = util::endian::little(msg.id); + plaintext.reportrate = util::endian::little(data.report_rate); + plaintext.type = data.motion_type; + + BOOST_LOG(verbose) << "Motion event state: "sv << msg.id << " :: "sv << util::hex(data.report_rate).to_string_view() << " :: "sv << util::hex(data.motion_type).to_string_view(); + std::array + encrypted_payload; + + payload = encode_control(session, util::view(plaintext), encrypted_payload); + } + else if (msg.type == platf::gamepad_feedback_e::set_rgb_led) { + control_set_rgb_led_t plaintext; + plaintext.header.type = packetTypes[IDX_SET_RGB_LED]; + plaintext.header.payloadLength = sizeof(plaintext) - sizeof(control_header_v2); + + auto &data = msg.data.rgb_led; + + plaintext.id = util::endian::little(msg.id); + plaintext.r = data.r; + plaintext.g = data.g; + plaintext.b = data.b; + + BOOST_LOG(verbose) << "RGB: "sv << msg.id << " :: "sv << util::hex(data.r).to_string_view() << util::hex(data.g).to_string_view() << util::hex(data.b).to_string_view(); + std::array + encrypted_payload; + + payload = encode_control(session, util::view(plaintext), encrypted_payload); + } + else { + BOOST_LOG(error) << "Unknown gamepad feedback message type"sv; return -1; } - BOOST_LOG(debug) << "Send gamepadnr ["sv << id << "] with lowfreq ["sv << lowfreq << "] and highfreq ["sv << highfreq << ']'; + if (session->broadcast_ref->control_server.send(payload, session->control.peer)) { + TUPLE_2D(port, addr, platf::from_sockaddr_ex((sockaddr *) &session->control.peer->address.address)); + BOOST_LOG(warning) << "Couldn't send gamepad feedback to ["sv << addr << ':' << port << ']'; + + return -1; + } return 0; } @@ -858,22 +958,20 @@ namespace stream { if (!session->control.peer) { has_session_awaiting_peer = true; } + else { + auto &feedback_queue = session->control.feedback_queue; + while (feedback_queue->peek()) { + auto feedback_msg = feedback_queue->pop(); - auto &rumble_queue = session->control.rumble_queue; - while (rumble_queue->peek()) { - auto rumble = rumble_queue->pop(); + send_feedback_msg(session, *feedback_msg); + } - send_rumble(session, rumble->id, rumble->lowfreq, rumble->highfreq); - } + auto &hdr_queue = session->control.hdr_queue; + while (session->control.peer && hdr_queue->peek()) { + auto hdr_info = hdr_queue->pop(); - // Unlike rumble which we send as best-effort, HDR state messages are critical - // for proper functioning of some clients. We must wait to pop entries from - // the queue until we're sure we have a peer to send them to. - auto &hdr_queue = session->control.hdr_queue; - while (session->control.peer && hdr_queue->peek()) { - auto hdr_info = hdr_queue->pop(); - - send_hdr_mode(session, std::move(hdr_info)); + send_hdr_mode(session, std::move(hdr_info)); + } } ++pos; @@ -1648,7 +1746,7 @@ namespace stream { session->config = config; - session->control.rumble_queue = mail->queue(mail::rumble); + session->control.feedback_queue = mail->queue(mail::gamepad_feedback); session->control.hdr_queue = mail->event(mail::hdr); session->control.iv = iv; session->control.cipher = crypto::cipher::gcm_t {