Skip to content

Commit

Permalink
Updates for .NET 8 (#23)
Browse files Browse the repository at this point in the history
* Update deps and add .NET 8 target

* Update Readme

* updated gh actions to use .NET 8 SDK

* update Logging to use Source Generators

* minor syntax adjustments

* Fix issue with bigger payloads

* update dependencies

* Drop not needed await

* address memory allocation concerns

* incorporate discord feedback

* fix double assignment

* check if buffer increase is enough
  • Loading branch information
Syzuna authored Apr 11, 2024
1 parent c59f15d commit d22efe0
Show file tree
Hide file tree
Showing 12 changed files with 75 additions and 114 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/check-buildstatus.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0.x
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build TwitchLib.EventSub.Websockets
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/preview-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0.x
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build TwitchLib.EventSub.Websockets
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0.x
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build TwitchLib.EventSub.Websockets
Expand Down
7 changes: 2 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,12 @@ EventSub via Websockets is still in open beta.
You can use it in production but Twitch may introduce breaking changes without prior notice.
The same goes for this implementation until it reaches Version 1.0.0

## Prerequisites
- Currently we only support setup via Dependency Injection

## Resources
If you need help on how to setup Dependency Injection in your Console or WPF Application you can have a look at these guides:
- Console: https://dfederm.com/building-a-console-app-with-.net-generic-host/
- WPF: https://laurentkempe.com/2019/09/03/WPF-and-dotnet-Generic-Host-with-dotnet-Core-3-0/

You can also find a console app example for .NET 6 and for .NET Framework 4.8 in the repo.
You can also find a console app example for .NET 8 and for .NET Framework 4.8 in the repo.

## Installation

Expand Down Expand Up @@ -124,4 +121,4 @@ namespace TwitchLib.EventSub.Websockets.Test

Alternatively you can also just clone the examples:
- .NET Framework 4.8 https://github.com/TwitchLib/TwitchLib.EventSub.Websockets/tree/main/TwitchLib.EventSub.Websockets.Example.NetStandard
- .NET 6 -> https://github.com/TwitchLib/TwitchLib.EventSub.Websockets/tree/main/TwitchLib.EventSub.Websockets.Example
- .NET 8 -> https://github.com/TwitchLib/TwitchLib.EventSub.Websockets/tree/main/TwitchLib.EventSub.Websockets.Example
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Worker">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<Nullable>disable</Nullable>
<ImplicitUsings>disable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
</ItemGroup>

<ItemGroup>
Expand Down
20 changes: 14 additions & 6 deletions TwitchLib.EventSub.Websockets/Client/WebsocketClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Threading.Tasks;
using TwitchLib.EventSub.Core;
using TwitchLib.EventSub.Websockets.Core.EventArgs;
using TwitchLib.EventSub.Websockets.Extensions;

#if NET6_0_OR_GREATER
using System.Buffers;
Expand All @@ -24,7 +25,7 @@ public class WebsocketClient : IDisposable
/// </summary>
public bool IsConnected => _webSocket.State == WebSocketState.Open;
/// <summary>
/// Determines if the Client is has encountered an unrecoverable issue based on WebsocketState
/// Determines if the Client has encountered an unrecoverable issue based on WebsocketState
/// </summary>
public bool IsFaulted => _webSocket.CloseStatus != WebSocketCloseStatus.Empty && _webSocket.CloseStatus != WebSocketCloseStatus.NormalClosure;

Expand Down Expand Up @@ -53,7 +54,7 @@ public async Task<bool> ConnectAsync(Uri url)
{
try
{
if (_webSocket.State == WebSocketState.Open || _webSocket.State == WebSocketState.Connecting)
if (_webSocket.State is WebSocketState.Open or WebSocketState.Connecting)
return true;

await _webSocket.ConnectAsync(url, CancellationToken.None);
Expand All @@ -79,7 +80,7 @@ public async Task<bool> DisconnectAsync()
{
try
{
if (_webSocket.State == WebSocketState.Open || _webSocket.State == WebSocketState.Connecting)
if (_webSocket.State is WebSocketState.Open or WebSocketState.Connecting)
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);

return true;
Expand Down Expand Up @@ -119,7 +120,12 @@ private async Task ProcessDataAsync()

if (payloadSize + receiveResult.Count >= storeSize)
{
storeSize *= 2;
storeSize +=
#if NET8_0_OR_GREATER
int.Max(4096, receiveResult.Count);
#else
Math.Max(4096, receiveResult.Count);
#endif
var newStore = MemoryPool<byte>.Shared.Rent(storeSize).Memory;
store.CopyTo(newStore);
store = newStore;
Expand Down Expand Up @@ -149,7 +155,7 @@ private async Task ProcessDataAsync()
case WebSocketMessageType.Binary:
break;
case WebSocketMessageType.Close:
_logger?.LogCritical($"{(WebSocketCloseStatus)_webSocket.CloseStatus!} - {_webSocket.CloseStatusDescription!}");
_logger?.LogWebsocketClosed((WebSocketCloseStatus)_webSocket.CloseStatus!, _webSocket.CloseStatusDescription!);
break;
default:
throw new ArgumentOutOfRangeException();
Expand Down Expand Up @@ -189,7 +195,9 @@ private async Task ProcessDataAsync()
if (buffer.Array == null)
continue;

#pragma warning disable CA1849
memory.Write(buffer.Array, buffer.Offset, receiveResult.Count);
#pragma warning restore CA1849
payloadSize += receiveResult.Count;
} while (!receiveResult.EndOfMessage);

Expand All @@ -214,7 +222,7 @@ private async Task ProcessDataAsync()
break;
case WebSocketMessageType.Close:
if (_webSocket.CloseStatus != null)
_logger?.LogCritical($"{(WebSocketCloseStatus) _webSocket.CloseStatus} - {_webSocket.CloseStatusDescription}");
_logger?.LogWebsocketClosed((WebSocketCloseStatus)_webSocket.CloseStatus!, _webSocket.CloseStatusDescription!);
break;
default:
throw new ArgumentOutOfRangeException();
Expand Down

This file was deleted.

37 changes: 19 additions & 18 deletions TwitchLib.EventSub.Websockets/EventSubWebsocketClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
using TwitchLib.EventSub.Websockets.Core.EventArgs.User;
using TwitchLib.EventSub.Websockets.Core.Handler;
using TwitchLib.EventSub.Websockets.Core.Models;
using TwitchLib.EventSub.Websockets.Core.NamingPolicies;
using TwitchLib.EventSub.Websockets.Extensions;

namespace TwitchLib.EventSub.Websockets
{
Expand Down Expand Up @@ -282,8 +282,8 @@ public class EventSubWebsocketClient
private readonly JsonSerializerOptions _jsonSerializerOptions = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = new SnakeCaseNamingPolicy(),
DictionaryKeyPolicy = new SnakeCaseNamingPolicy()
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
DictionaryKeyPolicy = JsonNamingPolicy.SnakeCaseLower
};

private const string WEBSOCKET_URL = "wss://eventsub.wss.twitch.tv/ws";
Expand Down Expand Up @@ -433,7 +433,7 @@ private async Task<bool> ReconnectAsync(Uri url)
await Task.Delay(100);
}

_logger?.LogError($"Websocket reconnect for {SessionId} failed!");
_logger?.LogReconnectFailed(SessionId);

return false;
}
Expand Down Expand Up @@ -509,7 +509,8 @@ private async Task OnDataReceived(object sender, DataReceivedArgs e)
_lastReceived = DateTimeOffset.Now;

var json = JsonDocument.Parse(e.Message);
var messageType = json.RootElement.GetProperty("metadata").GetProperty("message_type").GetString();
var metadata = json.RootElement.GetProperty("metadata"u8);
var messageType = metadata.GetProperty("message_type"u8).GetString();
switch (messageType)
{
case "session_welcome":
Expand All @@ -525,7 +526,7 @@ private async Task OnDataReceived(object sender, DataReceivedArgs e)
HandleKeepAlive(e.Message);
break;
case "notification":
var subscriptionType = json.RootElement.GetProperty("metadata").GetProperty("subscription_type").GetString();
var subscriptionType = metadata.GetProperty("subscription_type"u8).GetString();
if (string.IsNullOrWhiteSpace(subscriptionType))
{
await ErrorOccurred.InvokeAsync(this, new ErrorOccuredArgs { Exception = new ArgumentNullException(nameof(subscriptionType)), Message = "Unable to determine subscription type!" });
Expand All @@ -537,8 +538,8 @@ private async Task OnDataReceived(object sender, DataReceivedArgs e)
HandleRevocation(e.Message);
break;
default:
_logger?.LogWarning($"Unknown message type: {messageType}");
_logger?.LogDebug(e.Message);
_logger?.LogUnknownMessageType(messageType);
_logger?.LogMessage(e.Message);
break;
}
}
Expand All @@ -559,13 +560,13 @@ private async Task OnErrorOccurred(object sender, ErrorOccuredArgs e)
/// <param name="message">notification message received from Twitch EventSub</param>
private void HandleReconnect(string message)
{
_logger?.LogWarning($"Reconnect for {SessionId} requested!");
_logger?.LogReconnectRequested(SessionId);
var data = JsonSerializer.Deserialize<EventSubWebsocketSessionInfoMessage>(message, _jsonSerializerOptions);
_reconnectRequested = true;

Task.Run(async () => await ReconnectAsync(new Uri(data?.Payload.Session.ReconnectUrl ?? WEBSOCKET_URL)));

_logger?.LogDebug(message);
_logger?.LogMessage(message);
}

/// <summary>
Expand All @@ -585,11 +586,11 @@ private async ValueTask HandleWelcome(string message)
SessionId = data.Payload.Session.Id;
var keepAliveTimeout = data.Payload.Session.KeepaliveTimeoutSeconds + data.Payload.Session.KeepaliveTimeoutSeconds * 0.2;

_keepAliveTimeout = keepAliveTimeout.HasValue ? TimeSpan.FromSeconds(keepAliveTimeout.Value) : TimeSpan.FromSeconds(10);
_keepAliveTimeout = TimeSpan.FromSeconds(keepAliveTimeout ?? 10);

await WebsocketConnected.InvokeAsync(this, new WebsocketConnectedArgs { IsRequestedReconnect = _reconnectRequested });

_logger?.LogDebug(message);
_logger?.LogMessage(message);
}

/// <summary>
Expand All @@ -601,8 +602,8 @@ private async Task HandleDisconnect(string message)
var data = JsonSerializer.Deserialize<EventSubWebsocketSessionInfoMessage>(message);

if (data != null)
_logger?.LogCritical($"Websocket {data.Payload.Session.Id} disconnected at {data.Payload.Session.DisconnectedAt}. Reason: {data.Payload.Session.DisconnectReason}");

_logger?.LogForceDisconnected(data.Payload.Session.Id, data.Payload.Session.DisconnectedAt, data.Payload.Session.DisconnectReason);
await WebsocketDisconnected.InvokeAsync(this, EventArgs.Empty);
}

Expand All @@ -612,7 +613,7 @@ private async Task HandleDisconnect(string message)
/// <param name="message">notification message received from Twitch EventSub</param>
private void HandleKeepAlive(string message)
{
_logger?.LogDebug(message);
_logger?.LogMessage(message);
}

/// <summary>
Expand All @@ -625,7 +626,7 @@ private void HandleNotification(string message, string subscriptionType)
if (_handlers != null && _handlers.TryGetValue(subscriptionType, out var handler))
handler(this, message, _jsonSerializerOptions);

_logger?.LogDebug(message);
_logger?.LogMessage(message);
}

/// <summary>
Expand All @@ -637,7 +638,7 @@ private void HandleRevocation(string message)
if (_handlers != null && _handlers.TryGetValue("revocation", out var handler))
handler(this, message, _jsonSerializerOptions);

_logger?.LogDebug(message);
_logger?.LogMessage(message);
}

/// <summary>
Expand All @@ -649,7 +650,7 @@ internal void RaiseEvent(string eventName, object args = null)
{
var fInfo = GetType().GetField(eventName, BindingFlags.Instance | BindingFlags.NonPublic);

if (!(fInfo?.GetValue(this) is MulticastDelegate multi))
if (fInfo?.GetValue(this) is not MulticastDelegate multi)
return;

foreach (var del in multi.GetInvocationList())
Expand Down
28 changes: 28 additions & 0 deletions TwitchLib.EventSub.Websockets/Extensions/LogExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using System.Net.WebSockets;
using Microsoft.Extensions.Logging;
using TwitchLib.EventSub.Websockets.Client;

namespace TwitchLib.EventSub.Websockets.Extensions
{
internal static partial class LogExtensions
{
[LoggerMessage(LogLevel.Debug, "{message}")]
public static partial void LogMessage(this ILogger<EventSubWebsocketClient> logger, string message);

[LoggerMessage(LogLevel.Critical, "Websocket {sessionId} disconnected at {disconnectedAt}. Reason: {disconnectReason}")]
public static partial void LogForceDisconnected(this ILogger<EventSubWebsocketClient> logger, string sessionId, DateTime? disconnectedAt, string disconnectReason);

[LoggerMessage(LogLevel.Warning, "Websocket reconnect for SessionId {sessionId} requested!")]
public static partial void LogReconnectRequested(this ILogger<EventSubWebsocketClient> logger, string sessionId);

[LoggerMessage(LogLevel.Error, "Websocket reconnect for SessionId {sessionId} failed!")]
public static partial void LogReconnectFailed(this ILogger<EventSubWebsocketClient> logger, string sessionId);

[LoggerMessage(LogLevel.Warning, "Found unknown message type: {messageType}")]
public static partial void LogUnknownMessageType(this ILogger<EventSubWebsocketClient> logger, string messageType);

[LoggerMessage(LogLevel.Critical, "{closeStatus} - {closeStatusDescription}")]
public static partial void LogWebsocketClosed(this ILogger<WebsocketClient> logger, WebSocketCloseStatus closeStatus, string closeStatusDescription);
}
}
53 changes: 0 additions & 53 deletions TwitchLib.EventSub.Websockets/Extensions/StringExtensions.cs

This file was deleted.

Loading

0 comments on commit d22efe0

Please sign in to comment.