Skip to content

Commit

Permalink
Merge branch 'dev' into coro
Browse files Browse the repository at this point in the history
  • Loading branch information
Mishura4 authored Jul 11, 2024
2 parents e4dc9f3 + dc68a90 commit 6eef94a
Show file tree
Hide file tree
Showing 19 changed files with 127 additions and 46 deletions.
3 changes: 2 additions & 1 deletion .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@
"tparam",
"khanda",
"oclock",
"moai"
"moai",
"xbps"
],
"flagWords": [
"hte"
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
[![D++ CI](https://github.com/brainboxdotcc/DPP/actions/workflows/ci.yml/badge.svg)](https://github.com/brainboxdotcc/DPP/actions/workflows/ci.yml)
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/brainboxdotcc/DPP/badge)](https://securityscorecards.dev/viewer/?uri=github.com/brainboxdotcc/DPP)
[![AUR version](https://img.shields.io/aur/version/dpp)](https://aur.archlinux.org/packages/dpp)
[![XBPS version](https://repology.org/badge/version-for-repo/void_x86_64/dpp-discord.svg?header=xbps)](https://github.com/void-linux/void-packages/blob/master/srcpkgs/dpp/template)
![vcpkg version](https://img.shields.io/vcpkg/v/dpp)
[![Homebrew version](https://img.shields.io/homebrew/v/libdpp)](https://formulae.brew.sh/formula/libdpp#default)
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md)
Expand Down
4 changes: 2 additions & 2 deletions buildtools/classes/Generator/CoroGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public function getCommentArray(): array
*/
public function saveHeader(string $content): void
{
$content .= "[[nodiscard]] async<http_request_completion_t> co_request(const std::string &url, http_method method, const std::string &postdata = \"\", const std::string &mimetype = \"text/plain\", const std::multimap<std::string, std::string> &headers = {}, const std::string &protocol = \"1.1\");\n\n";
$content .= "[[nodiscard]] async<http_request_completion_t> co_request(const std::string &url, http_method method, const std::string &postdata = \"\", const std::string &mimetype = \"text/plain\", const std::multimap<std::string, std::string> &headers = {}, const std::string &protocol = \"1.1\", time_t request_timeout = 5);\n\n";
file_put_contents('include/dpp/cluster_coro_calls.h', $content);
}

Expand All @@ -125,7 +125,7 @@ public function saveHeader(string $content): void
*/
public function saveCpp(string $cppcontent): void
{
$cppcontent .= "dpp::async<dpp::http_request_completion_t> dpp::cluster::co_request(const std::string &url, http_method method, const std::string &postdata, const std::string &mimetype, const std::multimap<std::string, std::string> &headers, const std::string &protocol) {\n\treturn async<http_request_completion_t>{ [&, this] <typename C> (C &&cc) { return this->request(url, method, std::forward<C>(cc), postdata, mimetype, headers, protocol); }};\n}
$cppcontent .= "dpp::async<dpp::http_request_completion_t> dpp::cluster::co_request(const std::string &url, http_method method, const std::string &postdata, const std::string &mimetype, const std::multimap<std::string, std::string> &headers, const std::string &protocol, time_t request_timeout) {\n\treturn async<http_request_completion_t>{ [&, this] <typename C> (C &&cc) { return this->request(url, method, std::forward<C>(cc), postdata, mimetype, headers, protocol, request_timeout); }};\n}
#endif
";
Expand Down
2 changes: 1 addition & 1 deletion cmake/CPackSetup.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ set(CPACK_FREEBSD_PACKAGE_ORIGIN "misc/libdpp")
set(CPACK_RPM_PACKAGE_LICENSE "Apache 2.0")
set(CPACK_PACKAGE_CONTACT "https://discord.gg/dpp") # D++ Development Discord
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libsodium23 (>= 1.0.17-1), libopus0 (>= 1.3-1)")
set(CPACK_RPM_PACKAGE_REQUIRES "libsodium >= 1.0.17, opus >= 1.3.1")
set(CPACK_RPM_PACKAGE_REQUIRES "libsodium >= 1.0.20-1, opus >= 1.3.1")
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "An incredibly lightweight C++ Discord library")
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
set(CPACK_DEBIAN_PACKAGE_SECTION "libs")
Expand Down
1 change: 1 addition & 0 deletions docpages/03_installing.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ There are many ways to install D++, either from a package manager, or from sourc
* \subpage install-linux-rpm
* \subpage install-vcpkg
* \subpage install-arch-aur
* \subpage install-void-xbps
* \subpage install-windows-vs-zip
* \subpage install-xmake
* \subpage install-brew
Expand Down
43 changes: 43 additions & 0 deletions docpages/install/install-void-xbps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
\page install-void-xbps Installing from XBPS (Void Linux)

To install [D++ from XBPS source packages collection](https://github.com/void-linux/void-packages/blob/master/srcpkgs/dpp/template), follow the step below (as root):

```bash
xbps-install -Sy dpp-devel
```

This will install D++ development files and related libraries


You will now be able to use D++ by including its library on the command line:

```bash
g++ mybot.cpp -o mybot -ldpp
```

\include{doc} install_prebuilt_footer.dox

To build D++ with coroutine support as an XBPS package, follow the steps below. Ensure that [xbps-src is set up](https://github.com/void-linux/void-packages?tab=readme-ov-file#quick-start) beforehand:

```bash
# Inside the void-packages root folder
git checkout master && git pull
# Modifies the configure arguments to include coroutine support (-DDPP_CORO=ON)
grep -q 'configure_args=.*-DDPP_CORO=ON' srcpkgs/dpp/template || sed -i -e 's/\(configure_args="[^"]*\)"/\1 -DDPP_CORO=ON"/' srcpkgs/dpp/template
./xbps-src -K pkg dpp
```

Then as root:
```bash
# Inside the void-packages root folder
xbps-install --repository hostdir/binpkgs dpp-devel
```

This will do the following three things:
- Update the void-packages repository to the latest commit on master
- Patch the template file of D++ to enable coroutine support
- Build an XBPS package for D++ and install it

\note Cloning the void-packages repository may take some time

\include{doc} coro_warn.dox
3 changes: 2 additions & 1 deletion include/dpp/cluster.h
Original file line number Diff line number Diff line change
Expand Up @@ -1400,8 +1400,9 @@ class DPP_EXPORT cluster {
* @param mimetype MIME type of POST data
* @param headers Headers to send with the request
* @param protocol HTTP protocol to use (1.1 and 1.0 are supported)
* @param request_timeout How many seconds before the connection is considered failed if not finished
*/
void request(const std::string &url, http_method method, http_completion_event callback, const std::string &postdata = "", const std::string &mimetype = "text/plain", const std::multimap<std::string, std::string> &headers = {}, const std::string &protocol = "1.1");
void request(const std::string &url, http_method method, http_completion_event callback, const std::string &postdata = "", const std::string &mimetype = "text/plain", const std::multimap<std::string, std::string> &headers = {}, const std::string &protocol = "1.1", time_t request_timeout = 5);

/**
* @brief Respond to a slash command
Expand Down
2 changes: 1 addition & 1 deletion include/dpp/cluster_coro_calls.h
Original file line number Diff line number Diff line change
Expand Up @@ -2566,5 +2566,5 @@


/* End of auto-generated definitions */
[[nodiscard]] async<http_request_completion_t> co_request(const std::string &url, http_method method, const std::string &postdata = "", const std::string &mimetype = "text/plain", const std::multimap<std::string, std::string> &headers = {}, const std::string &protocol = "1.1");
[[nodiscard]] async<http_request_completion_t> co_request(const std::string &url, http_method method, const std::string &postdata = "", const std::string &mimetype = "text/plain", const std::multimap<std::string, std::string> &headers = {}, const std::string &protocol = "1.1", time_t request_timeout = 5);

9 changes: 7 additions & 2 deletions include/dpp/dispatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -1980,7 +1980,12 @@ struct DPP_EXPORT voice_buffer_send_t : public event_dispatch_t {
/**
* @brief encoded size of sent buffer
*/
int buffer_size = 0;
uint64_t buffer_size = 0;

/**
* @brief number of packet waiting to be sent in the queue
*/
size_t packets_left = 0;
};

/**
Expand Down Expand Up @@ -2079,7 +2084,7 @@ struct DPP_EXPORT voice_receive_t : public event_dispatch_t {
/**
* @brief Audio data, encoded as 48kHz stereo PCM or Opus,
*/
std::basic_string<uint8_t> audio_data = {};
std::vector<uint8_t> audio_data = {};

/**
* @brief User ID of speaker (zero if unknown)
Expand Down
5 changes: 5 additions & 0 deletions include/dpp/httpsclient.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,11 @@ class DPP_EXPORT https_client : public ssl_client {
http_state get_state();

public:
/**
* @brief If true the response timed out while waiting
*/
bool timed_out;

/**
* @brief Connect to a specific HTTP(S) server and complete a request.
*
Expand Down
8 changes: 7 additions & 1 deletion include/dpp/queues.h
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,11 @@ class DPP_EXPORT http_request {
*/
std::string protocol;

/**
* @brief How many seconds before the connection is considered failed if not finished
*/
time_t request_timeout;

/**
* @brief Constructor. When constructing one of these objects it should be passed to request_queue::post_request().
* @param _endpoint The API endpoint, e.g. /api/guilds
Expand Down Expand Up @@ -333,8 +338,9 @@ class DPP_EXPORT http_request {
* @param _mimetype POST data mime type
* @param _headers HTTP headers to send
* @param http_protocol HTTP protocol
* @param _request_timeout How many seconds before the connection is considered failed if not finished
*/
http_request(const std::string &_url, http_completion_event completion, http_method method = m_get, const std::string &_postdata = "", const std::string &_mimetype = "text/plain", const std::multimap<std::string, std::string> &_headers = {}, const std::string &http_protocol = "1.1");
http_request(const std::string &_url, http_completion_event completion, http_method method = m_get, const std::string &_postdata = "", const std::string &_mimetype = "text/plain", const std::multimap<std::string, std::string> &_headers = {}, const std::string &http_protocol = "1.1", time_t _request_timeout = 5);

/**
* @brief Destroy the http request object
Expand Down
8 changes: 5 additions & 3 deletions library/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,6 @@ if(NOT BUILD_SHARED_LIBS)
endif()
endif()

include("${CMAKE_CURRENT_SOURCE_DIR}/../cmake/colour.cmake")

if (BUILD_VOICE_SUPPORT)
if (MINGW OR NOT WIN32)
find_package(PkgConfig QUIET)
Expand Down Expand Up @@ -361,7 +359,11 @@ if (DPP_BUILD_TEST)
if (MSVC)
target_compile_options(${testname} PRIVATE /utf-8)
endif()
target_link_libraries(${testname} PUBLIC ${modname})
set (static_if_needed "")
if(NOT BUILD_SHARED_LIBS)
set (static_if_needed "-static")
endif()
target_link_libraries(${testname} PUBLIC ${modname} ${static_if_needed})
endif()
endforeach()
add_test(
Expand Down
4 changes: 2 additions & 2 deletions src/dpp/cluster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -382,8 +382,8 @@ void cluster::post_rest_multipart(const std::string &endpoint, const std::string
}


void cluster::request(const std::string &url, http_method method, http_completion_event callback, const std::string &postdata, const std::string &mimetype, const std::multimap<std::string, std::string> &headers, const std::string &protocol) {
raw_rest->post_request(std::make_unique<http_request>(url, callback, method, postdata, mimetype, headers, protocol));
void cluster::request(const std::string &url, http_method method, http_completion_event callback, const std::string &postdata, const std::string &mimetype, const std::multimap<std::string, std::string> &headers, const std::string &protocol, time_t request_timeout) {
raw_rest->post_request(std::make_unique<http_request>(url, callback, method, postdata, mimetype, headers, protocol, request_timeout));
}

gateway::gateway() : shards(0), session_start_total(0), session_start_remaining(0), session_start_reset_after(0), session_start_max_concurrency(0) {
Expand Down
4 changes: 2 additions & 2 deletions src/dpp/cluster_coro_calls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -851,8 +851,8 @@ async<confirmation_callback_t> cluster::co_get_webhook_with_token(snowflake webh
};

/* End of auto-generated definitions */
dpp::async<dpp::http_request_completion_t> dpp::cluster::co_request(const std::string &url, http_method method, const std::string &postdata, const std::string &mimetype, const std::multimap<std::string, std::string> &headers, const std::string &protocol) {
return async<http_request_completion_t>{ [&, this] <typename C> (C &&cc) { return this->request(url, method, std::forward<C>(cc), postdata, mimetype, headers, protocol); }};
dpp::async<dpp::http_request_completion_t> dpp::cluster::co_request(const std::string &url, http_method method, const std::string &postdata, const std::string &mimetype, const std::multimap<std::string, std::string> &headers, const std::string &protocol, time_t request_timeout) {
return async<http_request_completion_t>{ [&, this] <typename C> (C &&cc) { return this->request(url, method, std::forward<C>(cc), postdata, mimetype, headers, protocol, request_timeout); }};
}

#endif
42 changes: 22 additions & 20 deletions src/dpp/discordvoiceclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,11 +259,11 @@ void discord_voice_client::voice_courier_loop(discord_voice_client& client, cour
}
} else {
voice_receive_t& vr = *d.parked_payloads.top().vr;
if (vr.audio_data.length() > 0x7FFFFFFF) {
if (vr.audio_data.size() > 0x7FFFFFFF) {
throw dpp::length_exception(err_massive_audio, "audio_data > 2GB! This should never happen!");
}
if (samples = opus_decode(d.decoder.get(), vr.audio_data.data(),
static_cast<opus_int32>(vr.audio_data.length() & 0x7FFFFFFF), pcm, 5760, 0);
static_cast<opus_int32>(vr.audio_data.size() & 0x7FFFFFFF), pcm, 5760, 0);
samples >= 0) {
vr.reassign(&client, d.user_id, reinterpret_cast<uint8_t*>(pcm),
samples * opus_channel_count * sizeof(opus_int16));
Expand Down Expand Up @@ -709,22 +709,21 @@ void discord_voice_client::read_ready()
{
#ifdef HAVE_VOICE
uint8_t buffer[65535];
int r = this->udp_recv((char*)buffer, sizeof(buffer));
int packet_size = this->udp_recv((char*)buffer, sizeof(buffer));

if (r > 0 && (!creator->on_voice_receive.empty() || !creator->on_voice_receive_combined.empty())) {
const std::basic_string_view<uint8_t> packet{buffer, static_cast<size_t>(r)};
if (packet_size > 0 && (!creator->on_voice_receive.empty() || !creator->on_voice_receive_combined.empty())) {
constexpr size_t header_size = 12;
if (static_cast<size_t>(r) < header_size) {
if (static_cast<size_t>(packet_size) < header_size) {
/* Invalid RTP payload */
return;
}

/* It's a "silence packet" - throw it away. */
if (packet.size() < 44) {
if (packet_size < 44) {
return;
}

if (uint8_t payload_type = packet[1] & 0b0111'1111;
if (uint8_t payload_type = buffer[1] & 0b0111'1111;
72 <= payload_type && payload_type <= 76) {
/*
* This is an RTCP payload. Discord is known to send
Expand All @@ -737,43 +736,44 @@ void discord_voice_client::read_ready()

voice_payload vp{0, // seq, populate later
0, // timestamp, populate later
std::make_unique<voice_receive_t>(nullptr, std::string((char*)buffer, r))};
std::make_unique<voice_receive_t>(nullptr, std::string((char*)buffer, packet_size))};

vp.vr->voice_client = this;

{ /* Get the User ID of the speaker */
uint32_t speaker_ssrc;
std::memcpy(&speaker_ssrc, &packet[8], sizeof(uint32_t));
std::memcpy(&speaker_ssrc, &buffer[8], sizeof(uint32_t));
speaker_ssrc = ntohl(speaker_ssrc);
vp.vr->user_id = ssrc_map[speaker_ssrc];
}

/* Get the sequence number of the voice UDP packet */
std::memcpy(&vp.seq, &packet[2], sizeof(rtp_seq_t));
std::memcpy(&vp.seq, &buffer[2], sizeof(rtp_seq_t));
vp.seq = ntohs(vp.seq);
/* Get the timestamp of the voice UDP packet */
std::memcpy(&vp.timestamp, &packet[4], sizeof(rtp_timestamp_t));
std::memcpy(&vp.timestamp, &buffer[4], sizeof(rtp_timestamp_t));
vp.timestamp = ntohl(vp.timestamp);

/* Nonce is the RTP Header with zero padding */
uint8_t nonce[24] = { 0 };
std::memcpy(nonce, &packet[0], header_size);
std::memcpy(nonce, &buffer[0], header_size);

/* Get the number of CSRC in header */
const size_t csrc_count = packet[0] & 0b0000'1111;
const size_t csrc_count = buffer[0] & 0b0000'1111;
/* Skip to the encrypted voice data */
const ptrdiff_t offset_to_data = header_size + sizeof(uint32_t) * csrc_count;
uint8_t* encrypted_data = buffer + offset_to_data;
const size_t encrypted_data_len = r - offset_to_data;
const size_t encrypted_data_len = packet_size - offset_to_data;

if (crypto_secretbox_open_easy(encrypted_data, encrypted_data,
encrypted_data_len, nonce, secret_key)) {
/* Invalid Discord RTP payload. */
return;
}

std::basic_string_view<uint8_t> decrypted_data{encrypted_data, encrypted_data_len - crypto_box_MACBYTES};
if (const bool uses_extension [[maybe_unused]] = (packet[0] >> 4) & 0b0001) {
const uint8_t* decrypted_data = encrypted_data;
size_t decrypted_data_len = encrypted_data_len - crypto_box_MACBYTES;
if ([[maybe_unused]] const bool uses_extension = (buffer[0] >> 4) & 0b0001) {
/* Skip the RTP Extensions */
size_t ext_len = 0;
{
Expand All @@ -783,14 +783,15 @@ void discord_voice_client::read_ready()
ext_len = sizeof(uint32_t) * ext_len_in_words;
}
constexpr size_t ext_header_len = sizeof(uint16_t) * 2;
decrypted_data = decrypted_data.substr(ext_header_len + ext_len);
decrypted_data += ext_header_len + ext_len;
decrypted_data_len -= ext_header_len + ext_len;
}

/*
* We're left with the decrypted, opus-encoded data.
* Park the payload and decode on the voice courier thread.
*/
vp.vr->audio_data.assign(decrypted_data);
vp.vr->audio_data.assign(decrypted_data, decrypted_data + decrypted_data_len);

{
std::lock_guard lk(voice_courier_shared_state.mtx);
Expand Down Expand Up @@ -899,7 +900,8 @@ void discord_voice_client::write_ready()
last_timestamp = std::chrono::high_resolution_clock::now();
if (!creator->on_voice_buffer_send.empty()) {
voice_buffer_send_t snd(nullptr, "");
snd.buffer_size = (int)bufsize;
snd.buffer_size = bufsize;
snd.packets_left = outbuf.size();
snd.voice_client = this;
creator->on_voice_buffer_send.call(snd);
}
Expand Down
4 changes: 2 additions & 2 deletions src/dpp/dispatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,11 +279,11 @@ void voice_receive_t::reassign(discord_voice_client* vc, snowflake _user_id, con
voice_client = vc;
user_id = _user_id;

audio_data.assign(pcm, length);
audio_data.assign(pcm, pcm + length);

// for backwards compatibility; remove soon
audio = audio_data.data();
audio_size = audio_data.length();
audio_size = audio_data.size();
}

} // namespace dpp
7 changes: 6 additions & 1 deletion src/dpp/httpsclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ https_client::https_client(const std::string &hostname, uint16_t port, const st
request_headers(extra_headers),
status(0),
http_protocol(protocol),
timeout(request_timeout)
timeout(request_timeout),
timed_out(false)
{
nonblocking = false;
timeout = time(nullptr) + request_timeout;
Expand Down Expand Up @@ -313,6 +314,10 @@ http_state https_client::get_state() {

void https_client::one_second_timer() {
if ((this->sfd == SOCKET_ERROR || time(nullptr) >= timeout) && this->state != HTTPS_DONE) {
/* if and only if response is timed out */
if (this->sfd != SOCKET_ERROR) {
timed_out = true;
}
keepalive = false;
this->close();
}
Expand Down
Loading

0 comments on commit 6eef94a

Please sign in to comment.