Skip to content

Commit

Permalink
refactor!: Refactor player state tracking
Browse files Browse the repository at this point in the history
  • Loading branch information
angelobreuer committed Jan 29, 2024
1 parent 8ad95de commit 38cdfcb
Show file tree
Hide file tree
Showing 11 changed files with 106 additions and 110 deletions.
9 changes: 4 additions & 5 deletions src/Lavalink4NET.Tests/Players/LavalinkPlayerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
using Lavalink4NET.Protocol.Requests;
using Lavalink4NET.Rest;
using Lavalink4NET.Rest.Entities.Tracks;
using Lavalink4NET.Tracks;
using Microsoft.Extensions.Internal;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
Expand Down Expand Up @@ -913,25 +912,25 @@ protected override ValueTask NotifyFiltersUpdatedAsync(IPlayerFilters filters, C
return base.NotifyFiltersUpdatedAsync(filters, cancellationToken);
}

protected override ValueTask NotifyTrackEndedAsync(LavalinkTrack track, TrackEndReason endReason, CancellationToken cancellationToken = default)
protected override ValueTask NotifyTrackEndedAsync(ITrackQueueItem track, TrackEndReason endReason, CancellationToken cancellationToken = default)
{
TriggeredEvents.Add(nameof(NotifyTrackEndedAsync));
return base.NotifyTrackEndedAsync(track, endReason, cancellationToken);
}

protected override ValueTask NotifyTrackExceptionAsync(LavalinkTrack track, TrackException exception, CancellationToken cancellationToken = default)
protected override ValueTask NotifyTrackExceptionAsync(ITrackQueueItem track, TrackException exception, CancellationToken cancellationToken = default)
{
TriggeredEvents.Add(nameof(NotifyTrackExceptionAsync));
return base.NotifyTrackExceptionAsync(track, exception, cancellationToken);
}

protected override ValueTask NotifyTrackStartedAsync(LavalinkTrack track, CancellationToken cancellationToken = default)
protected override ValueTask NotifyTrackStartedAsync(ITrackQueueItem track, CancellationToken cancellationToken = default)
{
TriggeredEvents.Add(nameof(NotifyTrackStartedAsync));
return base.NotifyTrackStartedAsync(track, cancellationToken);
}

protected override ValueTask NotifyTrackStuckAsync(LavalinkTrack track, TimeSpan threshold, CancellationToken cancellationToken = default)
protected override ValueTask NotifyTrackStuckAsync(ITrackQueueItem track, TimeSpan threshold, CancellationToken cancellationToken = default)
{
TriggeredEvents.Add(nameof(NotifyTrackStuckAsync));
return base.NotifyTrackStuckAsync(track, threshold, cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Lavalink4NET.Players.Queued;
namespace Lavalink4NET.Players;

using Lavalink4NET.Tracks;

Expand Down
118 changes: 83 additions & 35 deletions src/Lavalink4NET/Players/LavalinkPlayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using Lavalink4NET.Clients;
using Lavalink4NET.Players.Queued;
using Lavalink4NET.Protocol;
using Lavalink4NET.Protocol.Models;
using Lavalink4NET.Protocol.Models.Filters;
Expand All @@ -31,6 +33,8 @@ public class LavalinkPlayer : ILavalinkPlayer, ILavalinkPlayerListener
private bool _disconnectOnDestroy;
private bool _connectedOnce;
private ulong _trackVersion;
private ITrackQueueItem? _nextTrack;
private ITrackQueueItem? _skippedTrack;

public LavalinkPlayer(IPlayerProperties<LavalinkPlayer, LavalinkPlayerOptions> properties)
{
Expand Down Expand Up @@ -72,6 +76,10 @@ public LavalinkPlayer(IPlayerProperties<LavalinkPlayer, LavalinkPlayerOptions> p
State = PlayerState.Playing;
}

_nextTrack = CurrentTrack is not null
? new TrackQueueItem(new TrackReference(CurrentTrack))
: null;

Refresh(properties.InitialState);
}

Expand Down Expand Up @@ -118,7 +126,9 @@ public TrackPosition? Position

public string Label { get; }

public LavalinkTrack? CurrentTrack { get; private set; }
public LavalinkTrack? CurrentTrack => CurrentItem?.Track;

public ITrackQueueItem? CurrentItem { get; protected internal set; }

private async ValueTask NotifyChannelUpdateCoreAsync(ulong? voiceChannelId, CancellationToken cancellationToken)
{
Expand Down Expand Up @@ -152,19 +162,20 @@ async ValueTask ILavalinkPlayerListener.NotifyTrackEndedAsync(LavalinkTrack trac
cancellationToken.ThrowIfCancellationRequested();
ArgumentNullException.ThrowIfNull(track);

Debug.Assert(State is PlayerState.Playing);

var currentTrackVersion = _trackVersion;
var previousTrack = CurrentTrack;
var previousItem = Interlocked.Exchange(ref _skippedTrack, null) ?? ResolveTrackQueueItem(track);

try
{
await NotifyTrackEndedAsync(track, endReason, cancellationToken).ConfigureAwait(false);
await NotifyTrackEndedAsync(previousItem, endReason, cancellationToken).ConfigureAwait(false);
}
finally
{
if (Volatile.Read(ref _trackVersion) == currentTrackVersion)
{
Debug.Assert(ReferenceEquals(previousTrack, CurrentTrack));
CurrentTrack = null;
CurrentItem = null;
await UpdateStateAsync(PlayerState.NotPlaying, cancellationToken).ConfigureAwait(false);
}
}
Expand All @@ -174,25 +185,30 @@ ValueTask ILavalinkPlayerListener.NotifyTrackExceptionAsync(LavalinkTrack track,
{
cancellationToken.ThrowIfCancellationRequested();
ArgumentNullException.ThrowIfNull(track);
return NotifyTrackExceptionAsync(track, exception, cancellationToken);
return NotifyTrackExceptionAsync(ResolveTrackQueueItem(track), exception, cancellationToken);
}

ValueTask ILavalinkPlayerListener.NotifyTrackStartedAsync(LavalinkTrack track, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
ArgumentNullException.ThrowIfNull(track);

CurrentTrack = track;
var nextTrack = Interlocked.Exchange(ref _nextTrack, null) ?? CurrentItem;

CurrentItem = track.Identifier == nextTrack?.Identifier
? nextTrack
: new TrackQueueItem(new TrackReference(track));

Interlocked.Increment(ref _trackVersion);

return NotifyTrackStartedAsync(track, cancellationToken);
return NotifyTrackStartedAsync(ResolveTrackQueueItem(track), cancellationToken);
}

ValueTask ILavalinkPlayerListener.NotifyTrackStuckAsync(LavalinkTrack track, TimeSpan threshold, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
ArgumentNullException.ThrowIfNull(track);
return NotifyTrackStuckAsync(track, threshold, cancellationToken);
return NotifyTrackStuckAsync(ResolveTrackQueueItem(track), threshold, cancellationToken);
}

public virtual async ValueTask PauseAsync(CancellationToken cancellationToken = default)
Expand All @@ -206,40 +222,27 @@ public virtual async ValueTask PauseAsync(CancellationToken cancellationToken =
_logger.PlayerPaused(Label);
}

public ValueTask PlayAsync(LavalinkTrack track, TrackPlayProperties properties = default, CancellationToken cancellationToken = default)
public virtual async ValueTask PlayAsync(ITrackQueueItem trackQueueItem, TrackPlayProperties properties = default, CancellationToken cancellationToken = default)
{
EnsureNotDestroyed();
cancellationToken.ThrowIfCancellationRequested();

return PlayAsync(new TrackReference(track), properties, cancellationToken);
}

public ValueTask PlayAsync(string identifier, TrackPlayProperties properties = default, CancellationToken cancellationToken = default)
{
EnsureNotDestroyed();
cancellationToken.ThrowIfCancellationRequested();

return PlayAsync(new TrackReference(identifier), properties, cancellationToken);
}

public virtual async ValueTask PlayAsync(TrackReference trackReference, TrackPlayProperties properties = default, CancellationToken cancellationToken = default)
{
EnsureNotDestroyed();
cancellationToken.ThrowIfCancellationRequested();
_skippedTrack = CurrentItem;
CurrentItem = _nextTrack = trackQueueItem;

var updateProperties = new PlayerUpdateProperties();

if (trackReference.IsPresent)
if (trackQueueItem.Reference.IsPresent)
{
var playableTrack = await trackReference.Track
var playableTrack = await trackQueueItem.Reference.Track
.GetPlayableTrackAsync(cancellationToken)
.ConfigureAwait(false);

updateProperties.TrackData = playableTrack.ToString();
}
else
{
updateProperties.Identifier = trackReference.Identifier;
updateProperties.Identifier = trackQueueItem.Reference.Identifier;
}

if (properties.StartPosition is not null)
Expand All @@ -255,6 +258,30 @@ public virtual async ValueTask PlayAsync(TrackReference trackReference, TrackPla
await PerformUpdateAsync(updateProperties, cancellationToken).ConfigureAwait(false);
}

public ValueTask PlayAsync(LavalinkTrack track, TrackPlayProperties properties = default, CancellationToken cancellationToken = default)
{
EnsureNotDestroyed();
cancellationToken.ThrowIfCancellationRequested();

return PlayAsync(new TrackQueueItem(new TrackReference(track)), properties, cancellationToken);
}

public ValueTask PlayAsync(string identifier, TrackPlayProperties properties = default, CancellationToken cancellationToken = default)
{
EnsureNotDestroyed();
cancellationToken.ThrowIfCancellationRequested();

return PlayAsync(new TrackQueueItem(new TrackReference(identifier)), properties, cancellationToken);
}

public ValueTask PlayAsync(TrackReference trackReference, TrackPlayProperties properties = default, CancellationToken cancellationToken = default)
{
EnsureNotDestroyed();
cancellationToken.ThrowIfCancellationRequested();

return PlayAsync(new TrackQueueItem(trackReference), properties, cancellationToken);
}

public async ValueTask RefreshAsync(CancellationToken cancellationToken = default)
{
EnsureNotDestroyed();
Expand Down Expand Up @@ -355,15 +382,15 @@ protected void EnsureNotDestroyed()

protected virtual ValueTask NotifyWebSocketClosedAsync(WebSocketCloseStatus closeStatus, string reason, bool byRemote = false, CancellationToken cancellationToken = default) => default;

protected virtual ValueTask NotifyTrackEndedAsync(LavalinkTrack track, TrackEndReason endReason, CancellationToken cancellationToken = default) => default;
protected virtual ValueTask NotifyTrackEndedAsync(ITrackQueueItem track, TrackEndReason endReason, CancellationToken cancellationToken = default) => default;

protected virtual ValueTask NotifyChannelUpdateAsync(ulong? voiceChannelId, CancellationToken cancellationToken = default) => default;

protected virtual ValueTask NotifyTrackExceptionAsync(LavalinkTrack track, TrackException exception, CancellationToken cancellationToken = default) => default;
protected virtual ValueTask NotifyTrackExceptionAsync(ITrackQueueItem track, TrackException exception, CancellationToken cancellationToken = default) => default;

protected virtual ValueTask NotifyTrackStartedAsync(LavalinkTrack track, CancellationToken cancellationToken = default) => default;
protected virtual ValueTask NotifyTrackStartedAsync(ITrackQueueItem track, CancellationToken cancellationToken = default) => default;

protected virtual ValueTask NotifyTrackStuckAsync(LavalinkTrack track, TimeSpan threshold, CancellationToken cancellationToken = default) => default;
protected virtual ValueTask NotifyTrackStuckAsync(ITrackQueueItem track, TimeSpan threshold, CancellationToken cancellationToken = default) => default;

protected virtual ValueTask NotifyFiltersUpdatedAsync(IPlayerFilters filters, CancellationToken cancellationToken = default) => default;

Expand Down Expand Up @@ -401,15 +428,15 @@ private void Refresh(PlayerInformationModel model)
if (model.CurrentTrack is null)
{
_currentTrackState = null;
CurrentTrack = null;
CurrentItem = null;
}
else
else if (model.CurrentTrack.Information.Identifier != CurrentItem?.Track?.Identifier)
{
_currentTrackState = model.CurrentTrack.Data;

var track = model.CurrentTrack.Information;

CurrentTrack = new LavalinkTrack
var currentTrack = new LavalinkTrack
{
Author = track.Author,
Identifier = track.Identifier,
Expand All @@ -425,6 +452,8 @@ private void Refresh(PlayerInformationModel model)
TrackData = model.CurrentTrack.Data,
AdditionalInformation = model.CurrentTrack.AdditionalInformation,
};

CurrentItem = new TrackQueueItem(new TrackReference(currentTrack));
}

Interlocked.Increment(ref _trackVersion);
Expand Down Expand Up @@ -613,6 +642,25 @@ private ValueTask UpdateVoiceCredentialsAsync(CancellationToken cancellationToke

return PerformUpdateAsync(properties, cancellationToken);
}

[return: NotNullIfNotNull(nameof(track))]
private ITrackQueueItem? ResolveTrackQueueItem(LavalinkTrack? track)
{
if (track is null)
{
Debug.Assert(CurrentItem is null);
return null;
}

Debug.Assert(track.Identifier == CurrentItem?.Track?.Identifier);

if (track.Identifier == CurrentItem?.Track?.Identifier)
{
return CurrentItem;
}

return new TrackQueueItem(new TrackReference(track));
}
}

internal static partial class Logging
Expand Down
1 change: 1 addition & 0 deletions src/Lavalink4NET/Players/Queued/IQueuedLavalinkPlayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

using System.Threading;
using System.Threading.Tasks;
using Lavalink4NET.Players;
using Lavalink4NET.Tracks;

public interface IQueuedLavalinkPlayer : ILavalinkPlayer
Expand Down
1 change: 1 addition & 0 deletions src/Lavalink4NET/Players/Queued/ITrackCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Lavalink4NET.Players;

public interface ITrackCollection : IReadOnlyList<ITrackQueueItem>
{
Expand Down
1 change: 1 addition & 0 deletions src/Lavalink4NET/Players/Queued/ITrackQueue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using Lavalink4NET.Players;

public interface ITrackQueue : ITrackCollection
{
Expand Down
Loading

0 comments on commit 38cdfcb

Please sign in to comment.