Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Websocket API #11

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
34 changes: 32 additions & 2 deletions BeatSaverSharp/BeatSaver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using BeatSaverSharp.Websocket;
using IWebsocketClientLite.PCL;
using Newtonsoft.Json;

namespace BeatSaverSharp
{
Expand All @@ -23,11 +27,13 @@ public class BeatSaver : IDisposable

private BeatSaverOptions _options;
private readonly IHttpService _httpService;
private readonly IWebsocketClient _websocketClient;
private readonly object _bLock = new object();
private readonly object _uLock = new object();
private readonly object _pLock = new object();
private static readonly (string, PropertyInfo)[] _filterProperties;
private static readonly (string, PropertyInfo)[] _playlistFilterProperties;
private static readonly JsonSerializer _jsonSerializer = new JsonSerializer();
private readonly ConcurrentDictionary<int, User> _fetchedUsers = new ConcurrentDictionary<int, User>();
private readonly ConcurrentDictionary<string, User> _fetchedUsernames = new ConcurrentDictionary<string, User>();
private readonly ConcurrentDictionary<string, Beatmap> _fetchedBeatmaps = new ConcurrentDictionary<string, Beatmap>();
Expand Down Expand Up @@ -68,6 +74,8 @@ public BeatSaver(BeatSaverOptions beatSaverOptions)
#else
_httpService = new HttpClientService(beatSaverOptions.BeatSaverAPI.ToString(), beatSaverOptions.Timeout, userAgent);
#endif
_websocketClient = new WebsocketClient(beatSaverOptions.WebsocketAPI.ToString(), userAgent);
_websocketClient.MessageRecievedEvent += OnWebsocketMessageRecieved;
}

public BeatSaver(string applicationName, Version version) : this(new BeatSaverOptions(applicationName, version))
Expand Down Expand Up @@ -501,6 +509,25 @@ public async Task<VoteResponse> Vote(string levelHash, Vote.Type voteType, Vote.

#endregion

#region Websocket

public event Action<WsBeatmap>? WebsocketMessageRecievedEvent;

private void OnWebsocketMessageRecieved(IDataframe dataframe)
{
if (WebsocketMessageRecievedEvent == null || dataframe.Message == null)
return;

using StringReader reader = new StringReader(dataframe.Message);
using JsonTextReader jsonTextReader = new JsonTextReader(reader);
WsBeatmap wsBeatmap = _jsonSerializer.Deserialize<WsBeatmap>(jsonTextReader)!;
GetOrAddBeatmapToCache(wsBeatmap.Map, out var cachedAndOrBeatmap);
wsBeatmap.Map = cachedAndOrBeatmap;
WebsocketMessageRecievedEvent.Invoke(wsBeatmap);
}

#endregion

private void ProcessCache()
{
if (_options.MaximumCacheSize == null || _options.MaximumCacheSize == 0)
Expand Down Expand Up @@ -826,8 +853,11 @@ public void Clear()
public void Dispose()
{
GC.SuppressFinalize(this);
if (_httpService is IDisposable disposable)
disposable.Dispose();
_websocketClient.MessageRecievedEvent -= OnWebsocketMessageRecieved;
if (_httpService is IDisposable httpDisposable)
httpDisposable.Dispose();
if (_websocketClient is IDisposable wsDisposable)
wsDisposable.Dispose();
IsDisposed = true;
}
}
Expand Down
1 change: 1 addition & 0 deletions BeatSaverSharp/BeatSaverOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public class BeatSaverOptions
public Version Version { get; set; }
public string ApplicationName { get; set; }
public Uri BeatSaverAPI { get; set; } = new Uri("https://api.beatsaver.com/");
public Uri WebsocketAPI { get; set; } = new Uri("wss://ws.beatsaver.com/maps");
public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(30);
public bool Cache { get; set; } = true;

Expand Down
3 changes: 3 additions & 0 deletions BeatSaverSharp/BeatSaverSharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
</Description>
<PackageTags>beatsaber beatsaver</PackageTags>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>

<ItemGroup>
Expand All @@ -35,6 +36,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="WebsocketClientLite.PCL" Version="7.3.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>

Expand All @@ -57,4 +59,5 @@
</Reference>
</ItemGroup>

<Import Project="ILRepack.targets" Condition="'$(Configuration)' != 'Debug'" />
</Project>
54 changes: 54 additions & 0 deletions BeatSaverSharp/ILRepack.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="ILRepack" Version="2.*">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<Target Name="ILRepack"
AfterTargets="Build">

<PropertyGroup>
<WorkingDirectory>$(ProjectDir)$(OutputPath)</WorkingDirectory>
<ILRepackOutputDir>Merged\</ILRepackOutputDir>
<ILRepackOutput>$(ILRepackOutputDir)$(AssemblyName).dll</ILRepackOutput>
</PropertyGroup>

<ItemGroup>
<RepackInputAssemblies Include="$(TargetPath)"/>
<RepackInputAssemblies Include="HttpMachine.dll"/>
<RepackInputAssemblies Include="IHttpMachine.dll"/>
<RepackInputAssemblies Include="IWebsocketClientLite.dll"/>
<RepackInputAssemblies Include="WebsocketClientLite.dll"/>
</ItemGroup>

<!-- ReferencePath contains actual files which can be referenced, we need directories -->
<ItemGroup>
<!-- Transform each entry into its directory -->
<_ReferenceDirsDupl Include="@(ReferencePath->'%(RelativeDir)')" />
</ItemGroup>
<!-- De-duplicate the directories for sanity -->
<RemoveDuplicates Inputs="@(_ReferenceDirsDupl)">
<Output TaskParameter="Filtered" ItemName="_ReferenceDirs" />
</RemoveDuplicates>

<Message Importance="low" Text="referencedirs @(_ReferenceDirs)" />

<ItemGroup>
<_ILRCmdArg Include="/union"/>
<_ILRCmdArg Include="/parallel"/>
<_ILRCmdArg Include="/internalize"/>
<_ILRCmdArg Include="/renameInternalized"/>
<_ILRCmdArg Include="/lib:%(_ReferenceDirs.Identity)" Condition="'$(OS)' != 'Windows_NT'" />
<_ILRCmdArg Include="/xmldocs"/>
<_ILRCmdArg Include="/out:$(ILRepackOutput)"/>
<_ILRCmdArg Include="%(RepackInputAssemblies.Identity)"/>
</ItemGroup>

<Exec WorkingDirectory="$(WorkingDirectory)"
Command="$(ILRepack) @(_ILRCmdArg->'&quot;%(Identity)&quot;', ' ')"/>

</Target>
</Project>
21 changes: 21 additions & 0 deletions BeatSaverSharp/Models/WsBeatmap.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace BeatSaverSharp.Models
{
public class WsBeatmap
{
[JsonConverter(typeof(StringEnumConverter))]
[JsonProperty("type")]
public WsMessageType Type { get; set; }

[JsonProperty("msg")]
public Beatmap Map { get; set; } = null!;
}

public enum WsMessageType
{
MAP_UPDATE,
MAP_DELETE
}
}
10 changes: 10 additions & 0 deletions BeatSaverSharp/Websocket/IWebsocketClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;
using IWebsocketClientLite.PCL;

namespace BeatSaverSharp.Websocket
{
public interface IWebsocketClient
{
public event Action<IDataframe>? MessageRecievedEvent;
}
}
40 changes: 40 additions & 0 deletions BeatSaverSharp/Websocket/WebsocketClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using IWebsocketClientLite.PCL;
using WebsocketClientLite.PCL;

namespace BeatSaverSharp.Websocket
{
internal class WebsocketClient : IWebsocketClient, IDisposable
{
private readonly MessageWebsocketRx _websocketLiteClient;
private readonly IDisposable _observable;
public event Action<IDataframe>? MessageRecievedEvent;

public WebsocketClient(string url, string userAgent)
{
_websocketLiteClient = new MessageWebsocketRx()
{
Headers = new Dictionary<string, string> {{ "User-Agent", userAgent }}
};

var websocketConnectionObservable = _websocketLiteClient.WebsocketConnectObservable(new Uri(url));
_observable = websocketConnectionObservable.Subscribe(OnNext);
}

private void OnNext(IDataframe? dataframe)
{
if (dataframe != null)
{
MessageRecievedEvent?.Invoke(dataframe);
}
}

public void Dispose()
{
GC.SuppressFinalize(this);
_websocketLiteClient.Dispose();
_observable.Dispose();
}
}
}