Skip to content

Commit

Permalink
Enhance interaction response and queue management
Browse files Browse the repository at this point in the history
- Introduced a new `EditResponseAsync` method for string content.
- Created `DefaultQueueEntry` class for track playback handling.
- Updated `LavalinkConfiguration` with a customizable `QueueEntryFactory`.
- Refactored `LavalinkGuildPlayer` to utilize a static `QueueInternal` for guild-specific queues.
- Deprecated the `QueueEntries` property in favor of the new `Queue` property.
- Implemented a new thread-safe `LavalinkQueue<T>` class for queue operations.
  • Loading branch information
hqtruong27 committed Jan 10, 2025
1 parent 050b0f3 commit 1e5541c
Show file tree
Hide file tree
Showing 5 changed files with 443 additions and 5 deletions.
8 changes: 8 additions & 0 deletions DisCatSharp.ApplicationCommands/Context/BaseContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,14 @@ public Task CreateInteractionIframeResponseAsync(string customId, string title,
public Task<DiscordMessage> EditResponseAsync(DiscordWebhookBuilder builder)
=> this.Interaction.EditOriginalResponseAsync(builder);

/// <summary>
/// Edits the interaction response.
/// </summary>
/// <param name="content">The content to edit the response with.</param>
/// <returns></returns>
public Task<DiscordMessage> EditResponseAsync(string content)
=> this.Interaction.EditOriginalResponseAsync(new DiscordWebhookBuilder().WithContent(content));

/// <summary>
/// Deletes the interaction response.
/// </summary>
Expand Down
11 changes: 11 additions & 0 deletions DisCatSharp.Lavalink/Entities/DefaultQueueEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Threading.Tasks;

namespace DisCatSharp.Lavalink.Entities;
public class DefaultQueueEntry : IQueueEntry
{
public LavalinkTrack Track { get; set; }

public Task<bool> BeforePlayingAsync(LavalinkGuildPlayer player) => Task.FromResult(true);

public Task AfterPlayingAsync(LavalinkGuildPlayer player) => Task.CompletedTask;
}
17 changes: 17 additions & 0 deletions DisCatSharp.Lavalink/LavalinkConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Net;

using DisCatSharp.Entities;
using DisCatSharp.Lavalink.Entities;
using DisCatSharp.Net;

using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -49,6 +50,7 @@ public LavalinkConfiguration(LavalinkConfiguration other)
this.EnableBuiltInQueueSystem = other.EnableBuiltInQueueSystem;
this.DefaultVolume = other.DefaultVolume;
this.EnableTrace = other.EnableTrace;
this.QueueEntryFactory = other.QueueEntryFactory;
}

/// <summary>
Expand Down Expand Up @@ -122,6 +124,21 @@ public LavalinkConfiguration(LavalinkConfiguration other)
/// </summary>
public bool EnableBuiltInQueueSystem { internal get; set; } = false;

/// <summary>
/// Sets a factory function that creates queue entry objects.
/// This allows you to customize the type of queue entry used by the LavalinkGuildPlayer when <see cref="EnableBuiltInQueueSystem"/> is <see langword="true"/>.
/// If set to <see langword="null"/>, the default queue entry type (<see cref="DefaultQueueEntry"/>) will be used.
/// </summary>
public Func<IQueueEntry>? QueueEntryFactory { get; set; }

/// <summary>
/// Gets a new instance of a queue entry object.
/// The type of the object is determined by the <see cref="QueueEntryFactory"/>.
/// If <see cref="QueueEntryFactory"/> is <see langword="null"/>, a <see cref="DefaultQueueEntry"/> instance is returned.
/// This property is only used when <see cref="EnableBuiltInQueueSystem"/> is set to <see langword="true"/>.
/// </summary>
public IQueueEntry QueueEntry => (this.QueueEntryFactory ?? (() => new DefaultQueueEntry()))();

/// <summary>
/// Sets whether trace should be enabled for more detailed error responses.
/// <para>Defaults to <see langword="false" />.</para>
Expand Down
145 changes: 140 additions & 5 deletions DisCatSharp.Lavalink/LavalinkGuildPlayer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
Expand All @@ -14,7 +15,6 @@
using DisCatSharp.Lavalink.Enums;
using DisCatSharp.Lavalink.EventArgs;
using DisCatSharp.Lavalink.Models;
using DisCatSharp.Lavalink.Payloads;
using DisCatSharp.Net.Abstractions;

using Microsoft.Extensions.Logging;
Expand Down Expand Up @@ -54,6 +54,13 @@ public sealed class LavalinkGuildPlayer
[SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "<Pending>")]
private readonly SortedList<string, IQueueEntry> _queueEntriesInternal = [];

internal static ConcurrentDictionary<ulong, LavalinkQueue<LavalinkTrack>> QueueInternal { get; } = new();

/// <summary>
/// Gets the queue.
/// </summary>
public IReadOnlyList<LavalinkTrack> Queue => QueueInternal.GetOrAdd(this.GuildId, new LavalinkQueue<LavalinkTrack>()).ToList();

/// <summary>
/// <see cref="TaskCompletionSource" /> for the queue.
/// </summary>
Expand All @@ -80,6 +87,8 @@ internal LavalinkGuildPlayer(LavalinkSession session, ulong guildId, LavalinkPla
this.VoiceStateUpdated += this.OnVoiceStateUpdated;
this.CurrentUsersInternal.Add(this.Discord.CurrentUser.Id, this.Discord.CurrentUser);
this.TrackEnded += this.OnTrackEnded;
if (this.QUEUE_SYSTEM_ENABLED)
QueueInternal.TryAdd(guildId, new());
}

/// <summary>
Expand Down Expand Up @@ -149,6 +158,7 @@ public int Ping
/// <summary>
/// Gets the queue entries.
/// </summary>
[Obsolete("This property is deprecated. Use Queue instead")]
public IReadOnlyList<IQueueEntry> QueueEntries
=> this._queueEntriesInternal.Values.ToList();

Expand Down Expand Up @@ -226,8 +236,12 @@ public event AsyncEventHandler<LavalinkGuildPlayer, LavalinkPlayerStateUpdateEve
/// <returns></returns>
private Task OnTrackEnded(LavalinkGuildPlayer sender, LavalinkTrackEndedEventArgs args)
{
if (this.QUEUE_SYSTEM_ENABLED && this._queueTsc != null)
this._queueTsc.SetResult(true);
if (this.QUEUE_SYSTEM_ENABLED)
{
this._queueTsc?.SetResult(true);
this.PlayNextInQueue();
}

if (this.CurrentTrack?.Info.Identifier == args.Track.Info.Identifier)
this.Player.Track = null;
args.Handled = false;
Expand Down Expand Up @@ -557,14 +571,15 @@ private Task OnVoiceStateUpdated(DiscordClient sender, VoiceStateUpdateEventArgs
return Task.CompletedTask;
}

#region Queue Operations
#region Queue Operations

/// <summary>
/// Adds a <see cref="LavalinkTrack" /> to the queue.
/// </summary>
/// <typeparam name="T">Queue entry object type.</typeparam>
/// <param name="entry">The entry to add. Please construct a new() entry for every track.</param>
/// <param name="track">The track to attach.</param>
[Obsolete("This method is deprecated. Use AddToQueue(LavalinkTrack track) instead. For custom actions before/after playback, consider using event handlers tied to track events or the new PlayQueue() logic.")]
public void AddToQueue<T>(T entry, LavalinkTrack track) where T : IQueueEntry
=> this._queueEntriesInternal.Add(track.Info.Uri.ToString(), entry.AddTrack(track));

Expand All @@ -573,6 +588,7 @@ public void AddToQueue<T>(T entry, LavalinkTrack track) where T : IQueueEntry
/// </summary>
/// <param name="entry">The entry to remove.</param>
/// <returns><see langword="true" /> if the entry was found and removed.</returns>
[Obsolete("This method is deprecated. Use RemoveQueue(LavalinkTrack track) or RemoveQueue(string identifier) instead, which directly manipulate the queue.")]
public bool RemoveFromQueue(IQueueEntry entry)
=> this._queueEntriesInternal.Remove(entry.Track.Info.Uri.ToString());

Expand All @@ -581,12 +597,14 @@ public bool RemoveFromQueue(IQueueEntry entry)
/// </summary>
/// <param name="identifier">The identifier to look up.</param>
/// <returns><see langword="true" /> if the entry was found and removed.</returns>
[Obsolete("This method is deprecated. Use RemoveQueue(string identifier) instead, which directly manipulates the queue.")]
public bool RemoveFromQueueByIdentifierAsync(string identifier)
=> this._queueEntriesInternal.Count != 0 && this._queueEntriesInternal!.TryGetValue(identifier, out var entry) && this.RemoveFromQueue(entry!);

/// <summary>
/// Plays the queue while entries are presented.
/// </summary>
[Obsolete("This method is deprecated. Use PlayQueue() instead, which utilizes the updated queueing system.")]
public async void PlayQueueAsync()
{
if (!this.QUEUE_SYSTEM_ENABLED)
Expand All @@ -611,5 +629,122 @@ public async void PlayQueueAsync()
}
}

#endregion
/// <summary>
/// Adds a Lavalink track to the queue.
/// </summary>
/// <param name="track">The track to add.</param>
public void AddToQueue(LavalinkTrack track)
=> QueueInternal.GetOrAdd(this.GuildId, new LavalinkQueue<LavalinkTrack>()).Enqueue(track);

/// <summary>
/// Attempts to retrieve the next track in the queue without removing it.
/// </summary>
/// <param name="track">The next track in the queue, or null if the queue is empty.</param>
/// <returns>True if a track was found, false otherwise.</returns>
public bool TryPeekQueue(out LavalinkTrack? track)
{
if (QueueInternal.TryGetValue(this.GuildId, out var queue) && queue.TryPeek(out var result))
{
track = result;
return true;
}

track = null;
return false;
}

/// <summary>
/// Shuffles the tracks in the queue.
/// </summary>
public void ShuffleQueue()
{
if (QueueInternal.TryGetValue(this.GuildId, out var queue))
{
queue.Shuffle();
}
}

/// <summary>
/// Reverses the order of the tracks in the queue.
/// </summary>
public void ReverseQueue()
{
if (QueueInternal.TryGetValue(this.GuildId, out var queue))
{
queue.Reverse();
}
}

/// <summary>
/// Removes the specified track from the queue.
/// </summary>
/// <param name="track">The track to remove.</param>
public void RemoveQueue(LavalinkTrack track)
{
if (QueueInternal.TryGetValue(this.GuildId, out var queue))
{
queue.Remove(track);
}
}

/// <summary>
/// Removes the track with the specified title (identifier) from the queue.
/// </summary>
/// <param name="identifier">The title (identifier) of the track to remove.</param>
public void RemoveQueue(string identifier)
{
if (QueueInternal.TryGetValue(this.GuildId, out var queue))
{
// Use Find to get the track and then remove it
var trackToRemove = queue.FirstOrDefault(x => x.Info.Title == identifier);
if (trackToRemove != null)
{
queue.Remove(trackToRemove);
}
}
}

/// <summary>
/// Clears all tracks from the queue.
/// </summary>
public void ClearQueue()
{
if (QueueInternal.TryGetValue(this.GuildId, out var queue))
{
queue.Clear();
}
}

/// <summary>
/// Starts playing the next track in the queue.
/// </summary>
public void PlayQueue()
{
if (!this.QUEUE_SYSTEM_ENABLED)
return;

this.PlayNextInQueue();
}

private async void PlayNextInQueue()
{
var queue = QueueInternal.GetOrAdd(this.GuildId, new LavalinkQueue<LavalinkTrack>());
if (queue.TryDequeue(out var nextTrack))
{
this._queueTsc = new();

var queueEntry = this.Session.Config.QueueEntry.AddTrack(nextTrack);

if (await queueEntry.BeforePlayingAsync(this).ConfigureAwait(false))
{
await this.PlayAsync(queueEntry.Track).ConfigureAwait(false);

await this._queueTsc.Task.ConfigureAwait(false);
this._queueTsc = null!;

await queueEntry.AfterPlayingAsync(this).ConfigureAwait(false);
}
}
}
#endregion
}
Loading

0 comments on commit 1e5541c

Please sign in to comment.