diff options
author | ouwou <26526779+ouwou@users.noreply.github.com> | 2023-01-03 19:01:33 -0500 |
---|---|---|
committer | ouwou <26526779+ouwou@users.noreply.github.com> | 2023-01-03 19:01:33 -0500 |
commit | 4740965f4c1ee879a733b4068f4026a0dfa626fd (patch) | |
tree | bf3f3e4cbac1e63fe9ef4f5191e553e8ce615fd7 /src | |
parent | 6ff2563e36eac4f90ba241df3937980abd73c579 (diff) | |
download | abaddon-portaudio-4740965f4c1ee879a733b4068f4026a0dfa626fd.tar.gz abaddon-portaudio-4740965f4c1ee879a733b4068f4026a0dfa626fd.zip |
rewrite DiscordVoiceClient and stuff
Diffstat (limited to 'src')
-rw-r--r-- | src/components/channels.cpp | 4 | ||||
-rw-r--r-- | src/discord/discord.cpp | 8 | ||||
-rw-r--r-- | src/discord/discord.hpp | 4 | ||||
-rw-r--r-- | src/discord/voiceclient.cpp | 326 | ||||
-rw-r--r-- | src/discord/voiceclient.hpp | 63 | ||||
-rw-r--r-- | src/discord/websocket.cpp | 17 | ||||
-rw-r--r-- | src/discord/websocket.hpp | 2 |
7 files changed, 214 insertions, 210 deletions
diff --git a/src/components/channels.cpp b/src/components/channels.cpp index a36b215..09e40c3 100644 --- a/src/components/channels.cpp +++ b/src/components/channels.cpp @@ -1019,7 +1019,7 @@ void ChannelList::OnVoiceChannelSubmenuPopup() { if (!iter) return; const auto id = static_cast<Snowflake>((*iter)[m_columns.m_id]); auto &discord = Abaddon::Get().GetDiscordClient(); - if (discord.IsConnectedToVoice()) { + if (discord.IsVoiceConnected() || discord.IsVoiceConnecting()) { m_menu_voice_channel_join.set_sensitive(false); m_menu_voice_channel_disconnect.set_sensitive(discord.GetVoiceChannelID() == id); } else { @@ -1040,7 +1040,7 @@ void ChannelList::OnDMSubmenuPopup() { m_menu_dm_toggle_mute.set_label("Mute"); #ifdef WITH_VOICE - if (discord.IsConnectedToVoice()) { + if (discord.IsVoiceConnected() || discord.IsVoiceConnecting()) { m_menu_dm_join_voice.set_sensitive(false); m_menu_dm_disconnect_voice.set_sensitive(discord.GetVoiceChannelID() == id); } else { diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp index cd16aa8..c068243 100644 --- a/src/discord/discord.cpp +++ b/src/discord/discord.cpp @@ -1194,10 +1194,14 @@ void DiscordClient::DisconnectFromVoice() { m_websocket.Send(m); } -bool DiscordClient::IsConnectedToVoice() const noexcept { +bool DiscordClient::IsVoiceConnected() const noexcept { return m_voice.IsConnected(); } +bool DiscordClient::IsVoiceConnecting() const noexcept { + return m_voice.IsConnecting(); +} + Snowflake DiscordClient::GetVoiceChannelID() const noexcept { return m_voice_channel_id; } @@ -2174,7 +2178,7 @@ void DiscordClient::HandleGatewayVoiceStateUpdate(const GatewayMessage &msg) { m_voice.SetSessionID(data.SessionID); // channel_id = null means disconnect. stop cuz out of order maybe - if (!data.ChannelID.has_value()) { + if (!data.ChannelID.has_value() && (m_voice.IsConnected() || m_voice.IsConnecting())) { m_voice.Stop(); } } else { diff --git a/src/discord/discord.hpp b/src/discord/discord.hpp index 2d8db91..1e500e9 100644 --- a/src/discord/discord.hpp +++ b/src/discord/discord.hpp @@ -183,7 +183,9 @@ public: #ifdef WITH_VOICE void ConnectToVoice(Snowflake channel_id); void DisconnectFromVoice(); - [[nodiscard]] bool IsConnectedToVoice() const noexcept; + // Is fully connected + [[nodiscard]] bool IsVoiceConnected() const noexcept; + [[nodiscard]] bool IsVoiceConnecting() const noexcept; [[nodiscard]] Snowflake GetVoiceChannelID() const noexcept; [[nodiscard]] std::unordered_set<Snowflake> GetUsersInVoiceChannel(Snowflake channel_id); [[nodiscard]] std::optional<uint32_t> GetSSRCOfUser(Snowflake id) const; diff --git a/src/discord/voiceclient.cpp b/src/discord/voiceclient.cpp index 765c2af..fe72f87 100644 --- a/src/discord/voiceclient.cpp +++ b/src/discord/voiceclient.cpp @@ -17,8 +17,8 @@ #endif // clang-format on -UDPSocket::UDPSocket() { - m_socket = socket(AF_INET, SOCK_DGRAM, 0); +UDPSocket::UDPSocket() + : m_socket(INVALID_SOCKET) { } UDPSocket::~UDPSocket() { @@ -30,6 +30,7 @@ void UDPSocket::Connect(std::string_view ip, uint16_t port) { m_server.sin_family = AF_INET; S_ADDR(m_server) = inet_addr(ip.data()); m_server.sin_port = htons(port); + m_socket = socket(AF_INET, SOCK_DGRAM, 0); bind(m_socket, reinterpret_cast<sockaddr *>(&m_server), sizeof(m_server)); } @@ -94,6 +95,11 @@ std::vector<uint8_t> UDPSocket::Receive() { } void UDPSocket::Stop() { + #ifdef _WIN32 + closesocket(m_socket); + #else + close(m_socket); + #endif m_running = false; if (m_thread.joinable()) m_thread.join(); } @@ -125,97 +131,62 @@ UDPSocket::type_signal_data UDPSocket::signal_data() { return m_signal_data; } -DiscordVoiceClient::DiscordVoiceClient() { +DiscordVoiceClient::DiscordVoiceClient() + : m_state(State::DisconnectedByClient) + , m_ws("voice-ws") + , m_log(spdlog::get("voice")) { if (sodium_init() == -1) { - spdlog::get("voice")->critical("sodium_init() failed"); + m_log->critical("sodium_init() failed"); } m_udp.signal_data().connect([this](const std::vector<uint8_t> &data) { OnUDPData(data); }); - m_dispatcher.connect([this]() { - m_dispatch_mutex.lock(); - if (m_message_queue.empty()) { - m_dispatch_mutex.unlock(); - return; - } - auto msg = std::move(m_message_queue.front()); - m_message_queue.pop(); - m_dispatch_mutex.unlock(); - OnGatewayMessage(msg); - }); + m_ws.signal_open().connect(sigc::mem_fun(*this, &DiscordVoiceClient::OnWebsocketOpen)); + m_ws.signal_close().connect(sigc::mem_fun(*this, &DiscordVoiceClient::OnWebsocketClose)); + m_ws.signal_message().connect(sigc::mem_fun(*this, &DiscordVoiceClient::OnWebsocketMessage)); + + m_dispatcher.connect(sigc::mem_fun(*this, &DiscordVoiceClient::OnDispatch)); + // idle or else singleton deadlock Glib::signal_idle().connect_once([this]() { - // cant put in ctor or deadlock in singleton initialization - auto &aud = Abaddon::Get().GetAudio(); - aud.SetOpusBuffer(m_opus_buffer.data()); - aud.signal_opus_packet().connect([this](int payload_size) { - if (m_connected) - m_udp.SendEncrypted(m_opus_buffer.data(), payload_size); + auto &audio = Abaddon::Get().GetAudio(); + audio.SetOpusBuffer(m_opus_buffer.data()); + audio.signal_opus_packet().connect([this](int payload_size) { + // SendEncrypted if udp is connected }); }); } DiscordVoiceClient::~DiscordVoiceClient() { - Stop(); + if (IsConnected() || IsConnecting()) Stop(); } void DiscordVoiceClient::Start() { - m_ws = std::make_unique<Websocket>("voice-ws"); - - m_ws->signal_open().connect([this]() { - spdlog::get("voice")->info("Websocket open"); - SetState(State::Opened); - }); - - m_ws->signal_close().connect([this](const ix::WebSocketCloseInfo &info) { - if (info.remote) { - SetState(State::ClosingByServer); - } - Stop(); - }); - - m_ws->signal_message().connect([this](const std::string &str) { - spdlog::get("voice-ws")->trace("Recv: {}", str); - std::lock_guard<std::mutex> _(m_dispatch_mutex); - m_message_queue.push(str); - m_dispatcher.emit(); - }); - - SetState(State::Opening); - m_ws->StartConnection("wss://" + m_endpoint + "/?v=7"); + SetState(State::ConnectingToWebsocket); m_heartbeat_waiter.revive(); m_keepalive_waiter.revive(); - m_connected = true; + m_ws.StartConnection("wss://" + m_endpoint + "/?v=7"); + m_signal_connected.emit(); } void DiscordVoiceClient::Stop() { - if (IsOpening() || IsOpened()) { - spdlog::get("voice")->debug("Requested voice client stop"); - SetState(State::ClosingByClient); - m_ws->Stop(); - } else if (IsClosing()) { - spdlog::get("voice")->debug("Completing stop in closing"); - if (m_state == State::ClosingByClient) { - SetState(State::ClosedByClient); - } else if (m_state == State::ClosingByServer) { - SetState(State::ClosedByServer); - } - m_ws->Stop(); - m_udp.Stop(); - m_heartbeat_waiter.kill(); - if (m_heartbeat_thread.joinable()) m_heartbeat_thread.join(); - m_keepalive_waiter.kill(); - if (m_keepalive_thread.joinable()) m_keepalive_thread.join(); - if (m_connected) { - m_connected = false; - m_signal_disconnected.emit(); - } - } else { - spdlog::get("voice")->debug("Stop called, but already stopped"); + if (!IsConnected() && !IsConnecting()) { + m_log->warn("Requested stop while not connected (from {})", GetStateName(m_state)); + return; } + + SetState(State::DisconnectedByClient); + m_ws.Stop(4014); + m_udp.Stop(); + m_heartbeat_waiter.kill(); + if (m_heartbeat_thread.joinable()) m_heartbeat_thread.join(); + m_keepalive_waiter.kill(); + if (m_keepalive_thread.joinable()) m_keepalive_thread.join(); + + m_signal_disconnected.emit(); } void DiscordVoiceClient::SetSessionID(std::string_view session_id) { @@ -239,37 +210,59 @@ void DiscordVoiceClient::SetUserID(Snowflake id) { } std::optional<uint32_t> DiscordVoiceClient::GetSSRCOfUser(Snowflake id) const { - if (const auto it = m_ssrc_map.find(id); it != m_ssrc_map.end()) { - return it->second; - } - return {}; + return std::nullopt; } bool DiscordVoiceClient::IsConnected() const noexcept { - return m_connected; + return m_state == State::Connected; +} + +bool DiscordVoiceClient::IsConnecting() const noexcept { + return m_state == State::ConnectingToWebsocket || m_state == State::EstablishingConnection; } void DiscordVoiceClient::OnGatewayMessage(const std::string &str) { VoiceGatewayMessage msg = nlohmann::json::parse(str); switch (msg.Opcode) { - case VoiceGatewayOp::Hello: { + case VoiceGatewayOp::Hello: HandleGatewayHello(msg); - } break; - case VoiceGatewayOp::Ready: { + break; + case VoiceGatewayOp::Ready: HandleGatewayReady(msg); - } break; - case VoiceGatewayOp::SessionDescription: { + break; + case VoiceGatewayOp::SessionDescription: HandleGatewaySessionDescription(msg); - } break; - case VoiceGatewayOp::Speaking: { + break; + case VoiceGatewayOp::Speaking: HandleGatewaySpeaking(msg); - } break; - default: break; + break; + default: + m_log->warn("Unhandled opcode: {}", static_cast<int>(msg.Opcode)); + } +} + +const char *DiscordVoiceClient::GetStateName(State state) { + switch (state) { + case State::DisconnectedByClient: + return "DisconnectedByClient"; + case State::DisconnectedByServer: + return "DisconnectedByServer"; + case State::ConnectingToWebsocket: + return "ConnectingToWebsocket"; + case State::EstablishingConnection: + return "EstablishingConnection"; + case State::Connected: + return "Connected"; + default: + return "Unknown"; } } void DiscordVoiceClient::HandleGatewayHello(const VoiceGatewayMessage &m) { VoiceHelloData d = m.Data; + + m_log->debug("Received hello: {}ms", d.HeartbeatInterval); + m_heartbeat_msec = d.HeartbeatInterval; m_heartbeat_thread = std::thread(&DiscordVoiceClient::HeartbeatThread, this); @@ -278,13 +271,16 @@ void DiscordVoiceClient::HandleGatewayHello(const VoiceGatewayMessage &m) { void DiscordVoiceClient::HandleGatewayReady(const VoiceGatewayMessage &m) { VoiceReadyData d = m.Data; + + m_log->debug("Received ready: {}:{} (ssrc: {})", d.IP, d.Port, d.SSRC); + m_ip = d.IP; m_port = d.Port; m_ssrc = d.SSRC; + if (std::find(d.Modes.begin(), d.Modes.end(), "xsalsa20_poly1305") == d.Modes.end()) { - spdlog::get("voice")->error("xsalsa20_poly1305 not in encryption modes"); + m_log->warn("xsalsa20_poly1305 not in modes"); } - spdlog::get("voice")->info("connect to {}:{} ssrc {}", m_ip, m_port, m_ssrc); m_udp.Connect(m_ip, m_port); m_keepalive_thread = std::thread(&DiscordVoiceClient::KeepaliveThread, this); @@ -294,29 +290,26 @@ void DiscordVoiceClient::HandleGatewayReady(const VoiceGatewayMessage &m) { void DiscordVoiceClient::HandleGatewaySessionDescription(const VoiceGatewayMessage &m) { VoiceSessionDescriptionData d = m.Data; - spdlog::get("voice")->debug("receiving with {}, secret key: {:ns}", d.Mode, spdlog::to_hex(std::begin(d.SecretKey), std::end(d.SecretKey))); + + m_log->debug("Received session description (mode: {}) (key: {:ns}) ", d.Mode, spdlog::to_hex(d.SecretKey.begin(), d.SecretKey.end())); VoiceSpeakingMessage msg; msg.Delay = 0; msg.SSRC = m_ssrc; msg.Speaking = VoiceSpeakingType::Microphone; - m_ws->Send(msg); + m_ws.Send(msg); m_secret_key = d.SecretKey; m_udp.SetSSRC(m_ssrc); m_udp.SetSecretKey(m_secret_key); m_udp.SendEncrypted({ 0xF8, 0xFF, 0xFE }); - m_udp.SendEncrypted({ 0xF8, 0xFF, 0xFE }); - m_udp.SendEncrypted({ 0xF8, 0xFF, 0xFE }); - m_udp.SendEncrypted({ 0xF8, 0xFF, 0xFE }); - m_udp.SendEncrypted({ 0xF8, 0xFF, 0xFE }); m_udp.Run(); } void DiscordVoiceClient::HandleGatewaySpeaking(const VoiceGatewayMessage &m) { - VoiceSpeakingData data = m.Data; - m_ssrc_map[data.UserID] = data.SSRC; - m_signal_speaking.emit(data); + VoiceSpeakingData d = m.Data; + // ssrc map + m_signal_speaking.emit(d); } void DiscordVoiceClient::Identify() { @@ -326,80 +319,92 @@ void DiscordVoiceClient::Identify() { msg.SessionID = m_session_id; msg.Token = m_token; msg.Video = true; - m_ws->Send(msg); + + m_ws.Send(msg); } void DiscordVoiceClient::Discovery() { std::vector<uint8_t> payload; - // 2 bytes = 1, request + // request payload.push_back(0x00); payload.push_back(0x01); - // 2 bytes = 70, pl length + // payload length (70) payload.push_back(0x00); - payload.push_back(70); - // 4 bytes = ssrc + payload.push_back(0x46); + // ssrc payload.push_back((m_ssrc >> 24) & 0xFF); payload.push_back((m_ssrc >> 16) & 0xFF); payload.push_back((m_ssrc >> 8) & 0xFF); payload.push_back((m_ssrc >> 0) & 0xFF); - // address and port - for (int i = 0; i < 66; i++) - payload.push_back(0); + // space for address and port + for (int i = 0; i < 66; i++) payload.push_back(0x00); + m_udp.Send(payload.data(), payload.size()); - auto response = m_udp.Receive(); - if (response.size() >= 74 && response[0] == 0x00 && response[1] == 0x02) { - const char *our_ip = reinterpret_cast<const char *>(&response[8]); - uint16_t our_port = (response[73] << 8) | response[74]; - spdlog::get("voice")->debug("IP address discovered: {}:{}\n", our_ip, our_port); - SelectProtocol(our_ip, our_port); - } else { - spdlog::get("voice")->error("Received non-discovery packet after discovery"); + + constexpr int MAX_TRIES = 100; + for (int i = 0; i < MAX_TRIES; i++) { + const auto response = m_udp.Receive(); + if (response.size() >= 74 && response[0] == 0x00 && response[1] == 0x02) { + const char *ip = reinterpret_cast<const char *>(response.data() + 8); + uint16_t port = (response[73] << 8) | response[74]; + m_log->info("Discovered IP and port: {}:{}", ip, port); + SelectProtocol(ip, port); + break; + } else { + m_log->error("Received non-discovery packet after sending request (try {}/{})", i + 1, MAX_TRIES); + } } } -void DiscordVoiceClient::SelectProtocol(std::string_view ip, uint16_t port) { +void DiscordVoiceClient::SelectProtocol(const char *ip, uint16_t port) { VoiceSelectProtocolMessage msg; msg.Mode = "xsalsa20_poly1305"; msg.Address = ip; msg.Port = port; msg.Protocol = "udp"; - m_ws->Send(msg); + + m_ws.Send(msg); } -void DiscordVoiceClient::OnUDPData(std::vector<uint8_t> data) { - uint8_t *payload = data.data() + 12; - uint32_t ssrc = (data[8] << 24) | - (data[9] << 16) | - (data[10] << 8) | - (data[11] << 0); - static std::array<uint8_t, 24> nonce = {}; - std::memcpy(nonce.data(), data.data(), 12); - if (crypto_secretbox_open_easy(payload, payload, data.size() - 12, nonce.data(), m_secret_key.data())) { - // spdlog::get("voice")->trace("UDP payload decryption failure"); +void DiscordVoiceClient::OnWebsocketOpen() { + m_log->info("Websocket opened"); + SetState(State::EstablishingConnection); +} + +void DiscordVoiceClient::OnWebsocketClose(const ix::WebSocketCloseInfo &info) { + if (info.remote) { + m_log->debug("Websocket closed (remote): {} ({})", info.code, info.reason); } else { - Abaddon::Get().GetAudio().FeedMeOpus(ssrc, { payload, payload + data.size() - 12 - crypto_box_MACBYTES }); + m_log->debug("Websocket closed (local): {} ({})", info.code, info.reason); } } +void DiscordVoiceClient::OnWebsocketMessage(const std::string &data) { + m_dispatch_mutex.lock(); + m_dispatch_queue.push(data); + m_dispatcher.emit(); + m_dispatch_mutex.unlock(); +} + void DiscordVoiceClient::HeartbeatThread() { while (true) { - if (!m_heartbeat_waiter.wait_for(std::chrono::milliseconds(m_heartbeat_msec))) - break; + if (!m_heartbeat_waiter.wait_for(std::chrono::milliseconds(m_heartbeat_msec))) break; + + const auto ms = static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::system_clock::now().time_since_epoch()) + .count()); - const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::system_clock::now().time_since_epoch()) - .count(); + m_log->trace("Heartbeat: {}", ms); VoiceHeartbeatMessage msg; - msg.Nonce = static_cast<uint64_t>(ms); - m_ws->Send(msg); + msg.Nonce = ms; + m_ws.Send(msg); } } void DiscordVoiceClient::KeepaliveThread() { while (true) { - if (!m_keepalive_waiter.wait_for(std::chrono::seconds(10))) - break; + if (!m_heartbeat_waiter.wait_for(std::chrono::seconds(10))) break; if (IsConnected()) { const static uint8_t KEEPALIVE[] = { 0x13, 0x37 }; @@ -409,44 +414,35 @@ void DiscordVoiceClient::KeepaliveThread() { } void DiscordVoiceClient::SetState(State state) { + m_log->debug("Changing state to {}", GetStateName(state)); m_state = state; - - switch (state) { - case State::Opening: - spdlog::get("voice")->debug("WS state: Opening"); - break; - case State::Opened: - spdlog::get("voice")->debug("WS state: Opened"); - break; - case State::ClosingByClient: - spdlog::get("voice")->debug("WS state: Closing (Client)"); - break; - case State::ClosingByServer: - spdlog::get("voice")->debug("WS state: Closing (Server)"); - break; - case State::ClosedByClient: - spdlog::get("voice")->debug("WS state: Closed (Client)"); - break; - case State::ClosedByServer: - spdlog::get("voice")->debug("WS state: Closed (Server)"); - break; - } -} - -bool DiscordVoiceClient::IsOpening() const noexcept { - return m_state == State::Opening; } -bool DiscordVoiceClient::IsOpened() const noexcept { - return m_state == State::Opened; -} - -bool DiscordVoiceClient::IsClosing() const noexcept { - return m_state == State::ClosingByClient || m_state == State::ClosingByServer; +void DiscordVoiceClient::OnUDPData(std::vector<uint8_t> data) { + uint8_t *payload = data.data() + 12; + uint32_t ssrc = (data[8] << 24) | + (data[9] << 16) | + (data[10] << 8) | + (data[11] << 0); + static std::array<uint8_t, 24> nonce = {}; + std::memcpy(nonce.data(), data.data(), 12); + if (crypto_secretbox_open_easy(payload, payload, data.size() - 12, nonce.data(), m_secret_key.data())) { + // spdlog::get("voice")->trace("UDP payload decryption failure"); + } else { + Abaddon::Get().GetAudio().FeedMeOpus(ssrc, { payload, payload + data.size() - 12 - crypto_box_MACBYTES }); + } } -bool DiscordVoiceClient::IsClosed() const noexcept { - return m_state == State::ClosedByClient || m_state == State::ClosedByServer; +void DiscordVoiceClient::OnDispatch() { + m_dispatch_mutex.lock(); + if (m_dispatch_queue.empty()) { + m_dispatch_mutex.unlock(); + return; + } + auto msg = std::move(m_dispatch_queue.front()); + m_dispatch_queue.pop(); + m_dispatch_mutex.unlock(); + OnGatewayMessage(msg); } DiscordVoiceClient::type_signal_disconnected DiscordVoiceClient::signal_connected() { diff --git a/src/discord/voiceclient.hpp b/src/discord/voiceclient.hpp index 61b329c..47caec6 100644 --- a/src/discord/voiceclient.hpp +++ b/src/discord/voiceclient.hpp @@ -11,6 +11,7 @@ #include <string> #include <glibmm/dispatcher.h> #include <sigc++/sigc++.h> +#include <spdlog/logger.h> #include <unordered_map> // clang-format on @@ -196,10 +197,21 @@ public: [[nodiscard]] std::optional<uint32_t> GetSSRCOfUser(Snowflake id) const; + // Is a websocket and udp connection fully established [[nodiscard]] bool IsConnected() const noexcept; + [[nodiscard]] bool IsConnecting() const noexcept; private: - void OnGatewayMessage(const std::string &str); + enum class State { + ConnectingToWebsocket, + EstablishingConnection, + Connected, + DisconnectedByClient, + DisconnectedByServer, + }; + static const char *GetStateName(State state); + + void OnGatewayMessage(const std::string &msg); void HandleGatewayHello(const VoiceGatewayMessage &m); void HandleGatewayReady(const VoiceGatewayMessage &m); void HandleGatewaySessionDescription(const VoiceGatewayMessage &m); @@ -207,13 +219,19 @@ private: void Identify(); void Discovery(); - void SelectProtocol(std::string_view ip, uint16_t port); + void SelectProtocol(const char *ip, uint16_t port); - void OnUDPData(std::vector<uint8_t> data); + void OnWebsocketOpen(); + void OnWebsocketClose(const ix::WebSocketCloseInfo &info); + void OnWebsocketMessage(const std::string &str); void HeartbeatThread(); void KeepaliveThread(); + void SetState(State state); + + void OnUDPData(std::vector<uint8_t> data); + std::string m_session_id; std::string m_endpoint; std::string m_token; @@ -221,24 +239,12 @@ private: Snowflake m_channel_id; Snowflake m_user_id; + std::array<uint8_t, 32> m_secret_key; + std::string m_ip; uint16_t m_port; uint32_t m_ssrc; - std::unordered_map<Snowflake, uint32_t> m_ssrc_map; - - std::array<uint8_t, 32> m_secret_key; - - // this is a unique_ptr because Websocket/ixwebsocket seems to have some strange behavior - // and quite frankly i do not feel like figuring out what is wrong - // so using a unique_ptr will just let me nuke the whole thing and make a new one - std::unique_ptr<Websocket> m_ws; - UDPSocket m_udp; - - Glib::Dispatcher m_dispatcher; - std::queue<std::string> m_message_queue; - std::mutex m_dispatch_mutex; - int m_heartbeat_msec; Waiter m_heartbeat_waiter; std::thread m_heartbeat_thread; @@ -246,25 +252,18 @@ private: Waiter m_keepalive_waiter; std::thread m_keepalive_thread; - std::array<uint8_t, 1275> m_opus_buffer; + Websocket m_ws; + UDPSocket m_udp; - std::atomic<bool> m_connected = false; + Glib::Dispatcher m_dispatcher; + std::queue<std::string> m_dispatch_queue; + std::mutex m_dispatch_mutex; - enum class State { - Opening, - Opened, - ClosingByClient, - ClosedByClient, - ClosingByServer, - ClosedByServer, - }; + void OnDispatch(); - void SetState(State state); + std::array<uint8_t, 1275> m_opus_buffer; - [[nodiscard]] bool IsOpening() const noexcept; - [[nodiscard]] bool IsOpened() const noexcept; - [[nodiscard]] bool IsClosing() const noexcept; - [[nodiscard]] bool IsClosed() const noexcept; + std::shared_ptr<spdlog::logger> m_log; std::atomic<State> m_state; diff --git a/src/discord/websocket.cpp b/src/discord/websocket.cpp index 349913a..d40d057 100644 --- a/src/discord/websocket.cpp +++ b/src/discord/websocket.cpp @@ -20,11 +20,14 @@ Websocket::Websocket(const std::string &id) void Websocket::StartConnection(const std::string &url) { m_log->debug("Starting connection to {}", url); - m_websocket.disableAutomaticReconnection(); - m_websocket.setUrl(url); - m_websocket.setOnMessageCallback([this](auto &&msg) { OnMessage(std::forward<decltype(msg)>(msg)); }); - m_websocket.setExtraHeaders(ix::WebSocketHttpHeaders { { "User-Agent", m_agent } }); // idk if this actually works - m_websocket.start(); + + m_websocket = std::make_unique<ix::WebSocket>(); + + m_websocket->disableAutomaticReconnection(); + m_websocket->setUrl(url); + m_websocket->setOnMessageCallback([this](auto &&msg) { OnMessage(std::forward<decltype(msg)>(msg)); }); + m_websocket->setExtraHeaders(ix::WebSocketHttpHeaders { { "User-Agent", m_agent } }); // idk if this actually works + m_websocket->start(); } void Websocket::SetUserAgent(std::string agent) { @@ -46,13 +49,13 @@ void Websocket::Stop() { void Websocket::Stop(uint16_t code) { m_log->debug("Stopping with close code {}", code); - m_websocket.stop(code); + m_websocket-> stop(code); } void Websocket::Send(const std::string &str) { if (m_print_messages) m_log->trace("Send: {}", str); - m_websocket.sendText(str); + m_websocket->sendText(str); } void Websocket::Send(const nlohmann::json &j) { diff --git a/src/discord/websocket.hpp b/src/discord/websocket.hpp index 768121e..a77bf55 100644 --- a/src/discord/websocket.hpp +++ b/src/discord/websocket.hpp @@ -26,7 +26,7 @@ public: private: void OnMessage(const ix::WebSocketMessagePtr &msg); - ix::WebSocket m_websocket; + std::unique_ptr<ix::WebSocket> m_websocket; std::string m_agent; public: |