Skip to content

Commit 0195461

Browse files
quinchsMisha-133brunoamancio
authored
feat: libdave support (#3222)
* create bindings project * adjust structure * spite misha * integrate dave into audio client * working send/receive audio with dave * add doc section about libdave * make voice api model internal * add xml docs to new close codes * document new voice op codes * add xml docs to the libdave binding * fix docs samples * Update tools/Discord.Net.Dave/Utils.cs Co-authored-by: Mihail Gribkov <61027276+Misha-133@users.noreply.github.com> * cap. snake case -> pascal case for constants * switch to hard cast of JToken * add PATH to libdave docs * ensure log sink isn't gc collected * improve thread and memory safety * rename constant to pascal case * remove 'CChar' alias * use ext. and add project imports * add dave to gh workflow * fix incorrect opcode for dave * add logs for MLS failure * add docs to libdave binding * update to newer libdave api * update to latest libdave * update to latest libdave spec * (mostly) working * ok its fixed * update libdave docs * remove comments * ensure previous dave sessions are cleaned * readd set speaking call * update log to be synchronous * grammar * return rented array to pool * doc new streams and fix warning message * Fix voice receive: wrong port in SelectProtocol + missing DAVE decrypt layer on early streams (#3241) * Fix SelectProtocol sending local port instead of external port The UDP discovery response from Discord contains the external IP and port as seen by the voice server. SendSelectProtocol was passing UdpPort (the local socket port) instead of the external port from the discovery packet. Behind NAT, these differ, causing the voice server to send audio to the wrong port. Extract the external port from bytes 72-73 of the discovery response and pass it to SendSelectProtocol alongside the external IP. * Rebuild input streams after initial DAVE key exchange When a user joins a voice channel, SocketGuild.RepopulateAudioStreamsAsync fires StreamCreated before AudioClient.OnConnectingAsync initializes the DaveSessionManager. This means input streams are created without the DaveDecryptStream layer in the pipeline. Once the MLS key exchange completes and the initial transition fires, the encrypted audio reaches OpusDecodeStream directly — causing InvalidPacket errors since the data is still DAVE-encrypted. Add RebuildInputStreamsForDaveAsync to AudioClient which tears down existing streams and recreates them with the DaveDecryptStream layer. Call it from DaveSessionManager.PrepareProtocolTransitionAsync when the initial transition (transitionId == InitTransitionId) completes. * clean up some memory related things * more memory related improvements --------- Co-authored-by: Mihail Gribkov <61027276+Misha-133@users.noreply.github.com> Co-authored-by: Th3B0Y <brunoamancio.ti@gmail.com>
1 parent 414b1a0 commit 0195461

40 files changed

Lines changed: 3561 additions & 98 deletions

.github/workflows/dotnet.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ jobs:
6666
dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" --no-restore --no-build -v minimal -c Release -o ${{ env.ArtifactStagingDirectory }} /p:BuildNumber=${{ env.Suffix }} /p:IsTagBuild=${{ env.IsTagBuild }}
6767
dotnet pack "src\Discord.Net.Webhook\Discord.Net.Webhook.csproj" --no-restore --no-build -v minimal -c Release -o ${{ env.ArtifactStagingDirectory }} /p:BuildNumber=${{ env.Suffix }} /p:IsTagBuild=${{ env.IsTagBuild }}
6868
dotnet pack "src\Discord.Net.Interactions\Discord.Net.Interactions.csproj" --no-restore --no-build -v minimal -c Release -o ${{ env.ArtifactStagingDirectory }} /p:BuildNumber=${{ env.Suffix }} /p:IsTagBuild=${{ env.IsTagBuild }}
69+
dotnet pack "tools\Discord.Net.Dave\Discord.Net.Dave.csproj" --no-restore --no-build -v minimal -c Release -o ${{ env.ArtifactStagingDirectory }} /p:BuildNumber=${{ env.Suffix }} /p:IsTagBuild=${{ env.IsTagBuild }}
6970
# dotnet pack "experiment\Discord.Net.BuildOverrides\Discord.Net.BuildOverrides.csproj" --no-restore --no-build -v minimal -c Release -o ${{ env.ArtifactStagingDirectory }} /p:BuildNumber=${{ env.Suffix }} /p:IsTagBuild=${{ env.IsTagBuild }}
7071

7172
- name: Publish Artifacts

Discord.Net.sln

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Microsoft Visual Studio Solution File, Format Version 12.00
1+
Microsoft Visual Studio Solution File, Format Version 12.00
22
# Visual Studio Version 17
33
VisualStudioVersion = 17.1.31903.286
44
MinimumVisualStudioVersion = 10.0.40219.1
@@ -42,6 +42,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{BB59
4242
EndProject
4343
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discord.Net.BuildOverrides", "experiment\Discord.Net.BuildOverrides\Discord.Net.BuildOverrides.csproj", "{115F4921-B44D-4F69-996B-69796959C99D}"
4444
EndProject
45+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discord.Net.Dave", "tools\Discord.Net.Dave\Discord.Net.Dave.csproj", "{A9198992-68A2-4DE0-A871-EB1A9768A47F}"
46+
EndProject
47+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{5239E8F8-5B8B-48DF-BEA1-1C7745D25D08}"
48+
EndProject
4549
Global
4650
GlobalSection(SolutionConfigurationPlatforms) = preSolution
4751
Debug|Any CPU = Debug|Any CPU
@@ -244,6 +248,18 @@ Global
244248
{115F4921-B44D-4F69-996B-69796959C99D}.Release|x64.Build.0 = Release|Any CPU
245249
{115F4921-B44D-4F69-996B-69796959C99D}.Release|x86.ActiveCfg = Release|Any CPU
246250
{115F4921-B44D-4F69-996B-69796959C99D}.Release|x86.Build.0 = Release|Any CPU
251+
{A9198992-68A2-4DE0-A871-EB1A9768A47F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
252+
{A9198992-68A2-4DE0-A871-EB1A9768A47F}.Debug|Any CPU.Build.0 = Debug|Any CPU
253+
{A9198992-68A2-4DE0-A871-EB1A9768A47F}.Debug|x64.ActiveCfg = Debug|Any CPU
254+
{A9198992-68A2-4DE0-A871-EB1A9768A47F}.Debug|x64.Build.0 = Debug|Any CPU
255+
{A9198992-68A2-4DE0-A871-EB1A9768A47F}.Debug|x86.ActiveCfg = Debug|Any CPU
256+
{A9198992-68A2-4DE0-A871-EB1A9768A47F}.Debug|x86.Build.0 = Debug|Any CPU
257+
{A9198992-68A2-4DE0-A871-EB1A9768A47F}.Release|Any CPU.ActiveCfg = Release|Any CPU
258+
{A9198992-68A2-4DE0-A871-EB1A9768A47F}.Release|Any CPU.Build.0 = Release|Any CPU
259+
{A9198992-68A2-4DE0-A871-EB1A9768A47F}.Release|x64.ActiveCfg = Release|Any CPU
260+
{A9198992-68A2-4DE0-A871-EB1A9768A47F}.Release|x64.Build.0 = Release|Any CPU
261+
{A9198992-68A2-4DE0-A871-EB1A9768A47F}.Release|x86.ActiveCfg = Release|Any CPU
262+
{A9198992-68A2-4DE0-A871-EB1A9768A47F}.Release|x86.Build.0 = Release|Any CPU
247263
EndGlobalSection
248264
GlobalSection(SolutionProperties) = preSolution
249265
HideSolutionNode = FALSE
@@ -264,6 +280,7 @@ Global
264280
{B61AAE66-15CC-40E4-873A-C23E697C3411} = {BB59D5B5-E7B0-4BF4-8F82-D14431B2799B}
265281
{4A03840B-9EBE-47E3-89AB-E0914DF21AFB} = {BB59D5B5-E7B0-4BF4-8F82-D14431B2799B}
266282
{115F4921-B44D-4F69-996B-69796959C99D} = {CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A}
283+
{A9198992-68A2-4DE0-A871-EB1A9768A47F} = {5239E8F8-5B8B-48DF-BEA1-1C7745D25D08}
267284
EndGlobalSection
268285
GlobalSection(ExtensibilityGlobals) = postSolution
269286
SolutionGuid = {D2404771-EEC8-45F2-9D71-F3373F6C1495}

docs/guides/toc.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,11 @@
143143
- name: Bearer Tokens
144144
topicUid: Guides.BearerToken
145145
- name: Voice
146-
topicUid: Guides.Voice.SendingVoice
146+
items:
147+
- name: Connecting and Sending
148+
topicUid: Guides.Voice.SendingVoice
149+
- name: libdave Support
150+
topicUid: Guides.Voice.LibDave
147151
- name: Polls
148152
topicUid: Guides.Polls
149153
- name: Deployment

docs/guides/voice/libdave.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
uid: Guides.Voice.LibDave
3+
title: Using libdave e2ee
4+
---
5+
6+
# Setup
7+
8+
You'll need a build of Discord's [libdave](https://github.com/discord/libdave) library as either `libdave.dll`, `libdave.so` or `libdave.o` in your apps executing directory or `PATH`. The library will resolve `libdave` and start using it automatically.
9+
10+
## Force enable/disable
11+
12+
You can opt-in/opt-out of libdave with the following config option:
13+
14+
[!code-csharp[Opt-in to libdave](samples/libdave_config.cs)]
15+
16+
### libdave Log Sink
17+
18+
By default, `libdave` writes its logs to standard output, you can override that by specifying a custom log sink:
19+
20+
[!code-csharp[libdave sink](samples/libdave_sink.cs)]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
var client = new DiscordSocketClient(new DiscordSocketConfig()
2+
{
3+
EnableVoiceDaveEncryption = true,
4+
...
5+
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Discord.LibDave.Dave.SetLogSink(MyLogSink);
2+
3+
void MyLogSink(
4+
Discord.LibDave.LoggingSeverity severity,
5+
string filePath,
6+
int lineNumber,
7+
string message
8+
)
9+
{
10+
// do what you want with the logs
11+
Console.WriteLine($"[{severity} | LIBDAVE @ {file}#{line}]: {message}");
12+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using Newtonsoft.Json;
2+
3+
namespace Discord.API.Voice;
4+
5+
internal class ClientsConnect
6+
{
7+
[JsonProperty("user_ids")]
8+
public ulong[] UserIds { get; set; }
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using Newtonsoft.Json;
2+
3+
namespace Discord.API.Voice;
4+
5+
internal class DaveMLSTransitionParams
6+
{
7+
[JsonProperty("transition_id")]
8+
public ushort TransitionId { get; set; }
9+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using Newtonsoft.Json;
2+
3+
namespace Discord.API.Voice;
4+
5+
internal class DavePrepareEpoch
6+
{
7+
[JsonProperty("protocol_version")]
8+
public ushort ProtocolVersion { get; set; }
9+
10+
[JsonProperty("epoch")]
11+
public ulong Epoch { get; set; }
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using Newtonsoft.Json;
2+
3+
namespace Discord.API.Voice;
4+
5+
internal class DavePrepareTransition
6+
{
7+
[JsonProperty("transition_id")]
8+
public ushort TransitionId { get; set; }
9+
10+
[JsonProperty("protocol_version")]
11+
public ushort ProtocolVersion { get; set; }
12+
}

0 commit comments

Comments
 (0)