Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 19 additions & 10 deletions NetCord/Gateway/GatewayClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -896,16 +896,18 @@ private protected override void OnConnected()

private ValueTask SendIdentifyAsync(ConnectionState connectionState, PresenceProperties? presence = null, CancellationToken cancellationToken = default)
{
var serializedPayload = new GatewayPayloadProperties<GatewayIdentifyProperties>(GatewayOpcode.Identify, new(Token.RawToken)
GatewayPayloadProperties<GatewayIdentifyProperties> payload = new(GatewayOpcode.Identify, new(Token.RawToken)
{
ConnectionProperties = _connectionProperties,
LargeThreshold = _largeThreshold,
Shard = Shard,
Presence = presence ?? _presence,
Intents = _intents,
}).Serialize(Serialization.Default.GatewayPayloadPropertiesGatewayIdentifyProperties);
});

_latencyTimer.Start();
return SendConnectionPayloadAsync(connectionState, serializedPayload, _internalTextPayloadProperties, cancellationToken);

return SendConnectionObjectAsync(connectionState, payload, Serialization.Default.GatewayPayloadPropertiesGatewayIdentifyProperties, _internalTextPayloadProperties, cancellationToken);
}

/// <summary>
Expand Down Expand Up @@ -948,16 +950,20 @@ private protected override ValueTask TryResumeAsync(ConnectionState connectionSt

private ValueTask TryResumeAsync(ConnectionState connectionState, string sessionId, int sequenceNumber, CancellationToken cancellationToken = default)
{
var serializedPayload = new GatewayPayloadProperties<GatewayResumeProperties>(GatewayOpcode.Resume, new(Token.RawToken, sessionId, sequenceNumber)).Serialize(Serialization.Default.GatewayPayloadPropertiesGatewayResumeProperties);
GatewayPayloadProperties<GatewayResumeProperties> payload = new(GatewayOpcode.Resume, new(Token.RawToken, sessionId, sequenceNumber));

_latencyTimer.Start();
return SendConnectionPayloadAsync(connectionState, serializedPayload, _internalTextPayloadProperties, cancellationToken);

return SendConnectionObjectAsync(connectionState, payload, Serialization.Default.GatewayPayloadPropertiesGatewayResumeProperties, _internalTextPayloadProperties, cancellationToken);
}

private protected override ValueTask HeartbeatAsync(ConnectionState connectionState, CancellationToken cancellationToken = default)
{
var serializedPayload = new GatewayPayloadProperties<int>(GatewayOpcode.Heartbeat, SequenceNumber).Serialize(Serialization.Default.GatewayPayloadPropertiesInt32);
GatewayPayloadProperties<int> payload = new(GatewayOpcode.Heartbeat, SequenceNumber);

_latencyTimer.Start();
return SendConnectionPayloadAsync(connectionState, serializedPayload, _internalTextPayloadProperties, cancellationToken);

return SendConnectionObjectAsync(connectionState, payload, Serialization.Default.GatewayPayloadPropertiesInt32, _internalTextPayloadProperties, cancellationToken);
}

private protected override ValueTask ProcessPayloadAsync(State state, ConnectionState connectionState, WebSocketMessageType messageType, ReadOnlySpan<byte> payload)
Expand Down Expand Up @@ -1037,7 +1043,8 @@ private async ValueTask HandlePayloadAsync(State state, ConnectionState connecti
public ValueTask UpdateVoiceStateAsync(VoiceStateProperties voiceState, WebSocketPayloadProperties? properties = null, CancellationToken cancellationToken = default)
{
GatewayPayloadProperties<VoiceStateProperties> payload = new(GatewayOpcode.VoiceStateUpdate, voiceState);
return SendPayloadAsync(payload.Serialize(Serialization.Default.GatewayPayloadPropertiesVoiceStateProperties), properties, cancellationToken);

return SendObjectAsync(payload, Serialization.Default.GatewayPayloadPropertiesVoiceStateProperties, properties, cancellationToken);
}

/// <summary>
Expand All @@ -1049,7 +1056,8 @@ public ValueTask UpdateVoiceStateAsync(VoiceStateProperties voiceState, WebSocke
public ValueTask UpdatePresenceAsync(PresenceProperties presence, WebSocketPayloadProperties? properties = null, CancellationToken cancellationToken = default)
{
GatewayPayloadProperties<PresenceProperties> payload = new(GatewayOpcode.PresenceUpdate, presence);
return SendPayloadAsync(payload.Serialize(Serialization.Default.GatewayPayloadPropertiesPresenceProperties), properties, cancellationToken);

return SendObjectAsync(payload, Serialization.Default.GatewayPayloadPropertiesPresenceProperties, properties, cancellationToken);
}

/// <summary>
Expand All @@ -1058,7 +1066,8 @@ public ValueTask UpdatePresenceAsync(PresenceProperties presence, WebSocketPaylo
public ValueTask RequestGuildUsersAsync(GuildUsersRequestProperties requestProperties, WebSocketPayloadProperties? properties = null, CancellationToken cancellationToken = default)
{
GatewayPayloadProperties<GuildUsersRequestProperties> payload = new(GatewayOpcode.RequestGuildUsers, requestProperties);
return SendPayloadAsync(payload.Serialize(Serialization.Default.GatewayPayloadPropertiesGuildUsersRequestProperties), properties, cancellationToken);

return SendObjectAsync(payload, Serialization.Default.GatewayPayloadPropertiesGuildUsersRequestProperties, properties, cancellationToken);
}

private async Task ProcessEventAsync(State state, ConnectionState connectionState, JsonGatewayPayload payload)
Expand Down
4 changes: 0 additions & 4 deletions NetCord/Gateway/GatewayPayloadProperties.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;

namespace NetCord.Gateway;

Expand All @@ -11,6 +9,4 @@ internal class GatewayPayloadProperties<T>(GatewayOpcode opcode, T d)

[JsonPropertyName("d")]
public T D { get; set; } = d;

public byte[] Serialize(JsonTypeInfo<GatewayPayloadProperties<T>> jsonTypeInfo) => JsonSerializer.SerializeToUtf8Bytes(this, jsonTypeInfo);
}
33 changes: 33 additions & 0 deletions NetCord/Gateway/RentedArrayBufferWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@ namespace NetCord.Gateway;

internal sealed class RentedArrayBufferWriter<T>(int minimumInitialCapacity) : IBufferWriter<T>, IDisposable
{
public readonly struct RentedBufferOwner(T[] buffer, int writtenCount) : IDisposable
{
public ReadOnlyMemory<T> WrittenMemory => buffer.AsMemory(0, writtenCount);

public ReadOnlySpan<T> WrittenSpan => buffer.AsSpan(0, writtenCount);

public int WrittenCount => writtenCount;

public void Dispose()
{
ArrayPool<T>.Shared.Return(buffer);
}
}

private int _index;

private T[] _buffer = ArrayPool<T>.Shared.Rent(minimumInitialCapacity);
Expand All @@ -23,6 +37,19 @@ public void Advance(int count)
_index += count;
}

public RentedBufferOwner ExtractBuffer()
{
var buffer = _buffer;

RentedBufferOwner rentedBufferOwner = new(buffer, _index);

_buffer = ArrayPool<T>.Shared.Rent(buffer.Length);

_index = 0;

return rentedBufferOwner;
}

public void Clear()
{
_index = 0;
Expand Down Expand Up @@ -52,13 +79,19 @@ private void ResizeBuffer(int sizeHint)

var buffer = _buffer;
int index = _index;

int sum = index + sizeHint;

if (buffer.Length < sum)
{
var pool = ArrayPool<T>.Shared;

var newBuffer = pool.Rent(sum);

buffer.AsSpan(0, index).CopyTo(newBuffer);

_buffer = newBuffer;

pool.Return(buffer);
}
}
Expand Down
8 changes: 4 additions & 4 deletions NetCord/Gateway/Voice/VoiceClient.DaveSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -367,9 +367,9 @@ private async ValueTask SendMlsKeyPackageAsync(ConnectionState connectionState)

private ValueTask SendTransitionReadyAsync(ConnectionState connectionState, ushort transitionId)
{
VoicePayloadProperties<DaveTransitionReadyProperties> readyPayload = new(VoiceOpcode.DaveTransitionReady, new(transitionId));
VoicePayloadProperties<DaveTransitionReadyProperties> payload = new(VoiceOpcode.DaveTransitionReady, new(transitionId));

return _client.SendConnectionPayloadAsync(connectionState, readyPayload.Serialize(Serialization.Default.VoicePayloadPropertiesDaveTransitionReadyProperties), _client._internalTextPayloadProperties);
return _client.SendConnectionObjectAsync(connectionState, payload, Serialization.Default.VoicePayloadPropertiesDaveTransitionReadyProperties, _client._internalTextPayloadProperties);
}

private ValueTask SendMlsCommitWelcomeAsync(ConnectionState connectionState, ReadOnlySpan<byte> commitWelcomeMessage)
Expand Down Expand Up @@ -397,9 +397,9 @@ static async ValueTask ContinueAsync(VoiceClient client, ConnectionState connect

private ValueTask SendMlsInvalidCommitWelcomeAsync(ConnectionState connectionState, ushort transitionId)
{
VoicePayloadProperties<DaveMlsInvalidCommitWelcomeProperties> invalidCommitWelcomePayload = new(VoiceOpcode.DaveMlsInvalidCommitWelcome, new(transitionId));
VoicePayloadProperties<DaveMlsInvalidCommitWelcomeProperties> payload = new(VoiceOpcode.DaveMlsInvalidCommitWelcome, new(transitionId));

return _client.SendConnectionPayloadAsync(connectionState, invalidCommitWelcomePayload.Serialize(Serialization.Default.VoicePayloadPropertiesDaveMlsInvalidCommitWelcomeProperties), _client._internalTextPayloadProperties);
return _client.SendConnectionObjectAsync(connectionState, payload, Serialization.Default.VoicePayloadPropertiesDaveMlsInvalidCommitWelcomeProperties, _client._internalTextPayloadProperties);
}

[SkipLocalsInit]
Expand Down
31 changes: 22 additions & 9 deletions NetCord/Gateway/Voice/VoiceClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,11 @@ public void Dispose()

private protected override ValueTask SendIdentifyAsync(ConnectionState connectionState, CancellationToken cancellationToken = default)
{
var serializedPayload = new VoicePayloadProperties<VoiceIdentifyProperties>(VoiceOpcode.Identify, new(GuildId, UserId, SessionId, Token, DaveSession.GetMaxSupportedProtocolVersion())).Serialize(Serialization.Default.VoicePayloadPropertiesVoiceIdentifyProperties);
VoicePayloadProperties<VoiceIdentifyProperties> payload = new(VoiceOpcode.Identify, new(GuildId, UserId, SessionId, Token, DaveSession.GetMaxSupportedProtocolVersion()));

_latencyTimer.Start();
return SendConnectionPayloadAsync(connectionState, serializedPayload, _internalTextPayloadProperties, cancellationToken);

return SendConnectionObjectAsync(connectionState, payload, Serialization.Default.VoicePayloadPropertiesVoiceIdentifyProperties, _internalTextPayloadProperties, cancellationToken);
}

private VoiceState CreateState()
Expand Down Expand Up @@ -201,16 +203,27 @@ private protected override ValueTask TryResumeAsync(ConnectionState connectionSt

private ValueTask TryResumeAsync(ConnectionState connectionState, int sequenceNumber, CancellationToken cancellationToken = default)
{
var serializedPayload = new VoicePayloadProperties<VoiceResumeProperties>(VoiceOpcode.Resume, new(GuildId, SessionId, Token, sequenceNumber)).Serialize(Serialization.Default.VoicePayloadPropertiesVoiceResumeProperties);
VoicePayloadProperties<VoiceResumeProperties> payload = new(VoiceOpcode.Resume, new(GuildId, SessionId, Token, sequenceNumber));

_latencyTimer.Start();
return SendConnectionPayloadAsync(connectionState, serializedPayload, _internalTextPayloadProperties, cancellationToken);

return SendConnectionObjectAsync(connectionState, payload, Serialization.Default.VoicePayloadPropertiesVoiceResumeProperties, _internalTextPayloadProperties, cancellationToken);
}

private protected override ValueTask HeartbeatAsync(ConnectionState connectionState, CancellationToken cancellationToken = default)
{
var serializedPayload = new VoicePayloadProperties<VoiceHeartbeatProperties>(VoiceOpcode.Heartbeat, new(Environment.TickCount, SequenceNumber)).Serialize(Serialization.Default.VoicePayloadPropertiesVoiceHeartbeatProperties);
VoicePayloadProperties<VoiceHeartbeatProperties> payload = new(VoiceOpcode.Heartbeat, new(Environment.TickCount, SequenceNumber));

_latencyTimer.Start();
return SendConnectionPayloadAsync(connectionState, serializedPayload, _internalTextPayloadProperties, cancellationToken);

return SendConnectionObjectAsync(connectionState, payload, Serialization.Default.VoicePayloadPropertiesVoiceHeartbeatProperties, _internalTextPayloadProperties, cancellationToken);
}

private ValueTask SelectProtocolAsync(ConnectionState connectionState, ProtocolProperties protocolProperties, CancellationToken cancellationToken = default)
{
VoicePayloadProperties<ProtocolProperties> payload = new(VoiceOpcode.SelectProtocol, protocolProperties);

return SendConnectionObjectAsync(connectionState, payload, Serialization.Default.VoicePayloadPropertiesProtocolProperties, _internalTextPayloadProperties, cancellationToken);
}

private protected override ValueTask ProcessPayloadAsync(State state, ConnectionState connectionState, WebSocketMessageType messageType, ReadOnlySpan<byte> payload)
Expand Down Expand Up @@ -370,8 +383,7 @@ private async ValueTask HandleJsonPayloadAsync(State state, ConnectionState conn

Log<object?>(LogLevel.Debug, null, null, static (s, e) => "Selecting a protocol.");

VoicePayloadProperties<ProtocolProperties> protocolPayload = new(VoiceOpcode.SelectProtocol, new("udp", new(externalIp, externalPort, encryptionName)));
await SendConnectionPayloadAsync(connectionState, protocolPayload.Serialize(Serialization.Default.VoicePayloadPropertiesProtocolProperties), _internalTextPayloadProperties).ConfigureAwait(false);
await SelectProtocolAsync(connectionState, new("udp", new(externalIp, externalPort, encryptionName))).ConfigureAwait(false);

await updateLatencyTask;
}
Expand Down Expand Up @@ -732,7 +744,8 @@ private bool TryGetVoiceData(RtpPacket packet, IVoiceEncryption encryption, Dave
public ValueTask EnterSpeakingStateAsync(SpeakingProperties speaking, WebSocketPayloadProperties? properties = null, CancellationToken cancellationToken = default)
{
VoicePayloadProperties<SpeakingProperties> payload = new(VoiceOpcode.Speaking, speaking);
return SendPayloadAsync(payload.Serialize(Serialization.Default.VoicePayloadPropertiesSpeakingProperties), properties, cancellationToken);

return SendObjectAsync(payload, Serialization.Default.VoicePayloadPropertiesSpeakingProperties, properties, cancellationToken);
}

/// <summary>
Expand Down
4 changes: 0 additions & 4 deletions NetCord/Gateway/Voice/VoicePayloadProperties.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;

namespace NetCord.Gateway.Voice;

Expand All @@ -11,6 +9,4 @@ internal class VoicePayloadProperties<T>(VoiceOpcode opcode, T d)

[JsonPropertyName("d")]
public T D { get; set; } = d;

public byte[] Serialize(JsonTypeInfo<VoicePayloadProperties<T>> jsonTypeInfo) => JsonSerializer.SerializeToUtf8Bytes(this, jsonTypeInfo);
}
16 changes: 16 additions & 0 deletions NetCord/Gateway/WebSocketClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;

using NetCord.Gateway.LatencyTimers;
using NetCord.Gateway.ReconnectStrategies;
Expand Down Expand Up @@ -610,6 +612,13 @@ private protected ValueTask AbortAndResumeAsync(State state, ConnectionState con
return ResumeAsync(state);
}

private protected async ValueTask SendObjectAsync<T>(T obj, JsonTypeInfo<T> jsonTypeInfo, WebSocketPayloadProperties? properties, CancellationToken cancellationToken = default)
{
using var output = WebSocketClientJsonSerializer.Serialize(obj, jsonTypeInfo);

await SendPayloadAsync(output.WrittenMemory, properties, cancellationToken).ConfigureAwait(false);
}

public async ValueTask SendPayloadAsync(ReadOnlyMemory<byte> buffer, WebSocketPayloadProperties? properties = null, CancellationToken cancellationToken = default)
{
var payloadProperties = _defaultTextPayloadProperties.Compose(properties);
Expand Down Expand Up @@ -658,6 +667,13 @@ public async ValueTask SendPayloadAsync(ReadOnlyMemory<byte> buffer, WebSocketPa
}
}

private protected async ValueTask SendConnectionObjectAsync<T>(ConnectionState connectionState, T obj, JsonTypeInfo<T> jsonTypeInfo, InternalWebSocketPayloadProperties payloadProperties, CancellationToken cancellationToken = default)
{
using var output = WebSocketClientJsonSerializer.Serialize(obj, jsonTypeInfo);

await SendConnectionPayloadAsync(connectionState, output.WrittenMemory, payloadProperties, cancellationToken).ConfigureAwait(false);
}

private protected async ValueTask SendConnectionPayloadAsync(ConnectionState connectionState, ReadOnlyMemory<byte> buffer, InternalWebSocketPayloadProperties payloadProperties, CancellationToken cancellationToken = default)
{
var exception = await TrySendConnectionPayloadAsync(connectionState, buffer, payloadProperties, cancellationToken).ConfigureAwait(false);
Expand Down
33 changes: 33 additions & 0 deletions NetCord/Gateway/WebSocketClientJsonSerializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;

namespace NetCord.Gateway;

internal static class WebSocketClientJsonSerializer
{
private readonly record struct State(Utf8JsonWriter Writer, RentedArrayBufferWriter<byte> Output);

private const int DefaultBufferSize = 1024;

[ThreadStatic]
private static State? t_state;

public static RentedArrayBufferWriter<byte>.RentedBufferOwner Serialize<T>(T value, JsonTypeInfo<T> jsonTypeInfo)
{
if (t_state is (var writer, var output))
{
writer.Reset();
output.Clear();
}
else
{
output = new(DefaultBufferSize);
writer = new(output);
t_state = new(writer, output);
}

JsonSerializer.Serialize(writer, value, jsonTypeInfo);

return output.ExtractBuffer();
}
}