Search code examples
c#servicediscord.net

Exception: Unable to resolve service for type while attempting to activate


'Unable to resolve service for type 'Victoria.LavaNode1[TheSwarmManager.Modules.XLavaPlayer]' while attempting to activate 'TheSwarmManager.Services.AudioHandler'.'`

So, I'm creating a discord bot using C#, Discord.NET and Victoria library for playing music. My services are being configured here:

private ServiceProvider ConfigureServices()
{
    return new ServiceCollection()
        .AddSingleton<DiscordSocketClient>()
        .AddSingleton(x => new DiscordSocketClient(new DiscordSocketConfig
        {
            GatewayIntents = Discord.GatewayIntents.All,
            AlwaysDownloadUsers = true,
        }))

        .AddSingleton<InteractionService>()
        .AddSingleton<InteractionHandler>()
        .AddSingleton<CommandService>()
        .AddSingleton<PrefixHandler>()

        .AddLavaNode()
        .AddSingleton(new LavaConfig())
        .AddSingleton<AudioHandler>()

        .AddSingleton(_config)

        .BuildServiceProvider();
}

The line that's causing exception is this:

_services = ConfigureServices();
_audioService = _services.GetRequiredService<AudioHandler>(); // <- this one

And here's the XLavaPlayer.cs file:

using Discord;
using Victoria;

namespace TheSwarmManager.Modules {
    public class XLavaPlayer : LavaPlayer {
        public string ChannelName { get; }
        public XLavaPlayer(LavaSocket lavaSocket, IVoiceChannel voiceChannel, ITextChannel textChannel)
            : base(lavaSocket, voiceChannel, textChannel) {
            ChannelName = textChannel.Name;
        }
    }
}

I tried to do something with AddScope but I'm kinda new, so I didn't go any further.

EDIT 1: I also think there might be something wrong with my AudioHandler, here's the code:

using System.Collections.Concurrent;
using Microsoft.Extensions.Logging;
using Victoria;
using Victoria.Enums;
using Victoria.EventArgs;
using TheSwarmManager.Modules;

namespace TheSwarmManager.Services {
    public sealed class AudioHandler {
        private readonly LavaNode<XLavaPlayer> _lavaNode;
        private readonly ILogger _logger;
        public readonly HashSet<ulong> VoteQueue;
        private readonly ConcurrentDictionary<ulong, CancellationTokenSource> _disconnectTokens;

        public Func<TrackEndedEventArgs, Task> TrackEnded { get; internal set; }

        public AudioHandler(LavaNode<XLavaPlayer> lavaNode, ILoggerFactory loggerFactory) {
            _lavaNode = lavaNode;
            _logger = loggerFactory.CreateLogger<LavaNode>();
            _disconnectTokens = new ConcurrentDictionary<ulong, CancellationTokenSource>();

            _lavaNode.OnPlayerUpdated += OnPlayerUpdated;
            _lavaNode.OnStatsReceived += OnStatsReceived;
            _lavaNode.OnTrackEnded += OnTrackEnded;
            _lavaNode.OnTrackStarted += OnTrackStarted;
            _lavaNode.OnTrackException += OnTrackException;
            _lavaNode.OnTrackStuck += OnTrackStuck;
            _lavaNode.OnWebSocketClosed += OnWebSocketClosed;

            VoteQueue = new HashSet<ulong>();
        }

        private Task OnPlayerUpdated(PlayerUpdateEventArgs arg) {
            _logger.LogInformation($"Track update received for {arg.Track.Title}: {arg.Position}");
            return Task.CompletedTask;
        }

        private Task OnStatsReceived(StatsEventArgs arg) {
            _logger.LogInformation($"Lavalink has been up for {arg.Uptime}.");
            return Task.CompletedTask;
        }

        private async Task OnTrackStarted(TrackStartEventArgs arg) {
            await arg.Player.TextChannel.SendMessageAsync($"Now playing: {arg.Track.Title}");
            if (!_disconnectTokens.TryGetValue(arg.Player.VoiceChannel.Id, out var value)) {
                return;
            }

            if (value.IsCancellationRequested) {
                return;
            }

            value.Cancel(true);
            await arg.Player.TextChannel.SendMessageAsync("Auto disconnect has been cancelled!");
        }

        private async Task OnTrackEnded(TrackEndedEventArgs args) {
            if (args.Reason != TrackEndReason.Finished) {
                return;
            }

            var player = args.Player;
            if (!player.Queue.TryDequeue(out var lavaTrack)) {
                await player.TextChannel.SendMessageAsync("Queue completed! Please add more tracks to rock n' roll!");
                _ = InitiateDisconnectAsync(args.Player, TimeSpan.FromSeconds(10));
                return;
            }

            if (lavaTrack is null) {
                await player.TextChannel.SendMessageAsync("Next item in queue is not a track.");
                return;
            }

            await args.Player.PlayAsync(lavaTrack);
            await args.Player.TextChannel.SendMessageAsync(
                $"{args.Reason}: {args.Track.Title}\nNow playing: {lavaTrack.Title}");
        }

        private async Task InitiateDisconnectAsync(LavaPlayer player, TimeSpan timeSpan) {
            if (!_disconnectTokens.TryGetValue(player.VoiceChannel.Id, out var value)) {
                value = new CancellationTokenSource();
                _disconnectTokens.TryAdd(player.VoiceChannel.Id, value);
            }
            else if (value.IsCancellationRequested) {
                _disconnectTokens.TryUpdate(player.VoiceChannel.Id, new CancellationTokenSource(), value);
                value = _disconnectTokens[player.VoiceChannel.Id];
            }

            await player.TextChannel.SendMessageAsync($"Auto disconnect initiated! Disconnecting in {timeSpan}...");
            var isCancelled = SpinWait.SpinUntil(() => value.IsCancellationRequested, timeSpan);
            if (isCancelled) {
                return;
            }

            await _lavaNode.LeaveAsync(player.VoiceChannel);
            await player.TextChannel.SendMessageAsync("Invite me again sometime, sugar.");
        }

        private async Task OnTrackException(TrackExceptionEventArgs arg) {
            _logger.LogError($"Track {arg.Track.Title} threw an exception. Please check Lavalink console/logs.");
            arg.Player.Queue.Enqueue(arg.Track);
            await arg.Player.TextChannel.SendMessageAsync(
                $"{arg.Track.Title} has been re-added to queue after throwing an exception.");
        }

        private async Task OnTrackStuck(TrackStuckEventArgs arg) {
            _logger.LogError(
                $"Track {arg.Track.Title} got stuck for {arg.Threshold}ms. Please check Lavalink console/logs.");
            arg.Player.Queue.Enqueue(arg.Track);
            await arg.Player.TextChannel.SendMessageAsync(
                $"{arg.Track.Title} has been re-added to queue after getting stuck.");
        }

        private Task OnWebSocketClosed(WebSocketClosedEventArgs arg) {
            _logger.LogCritical($"Discord WebSocket connection closed with following reason: {arg.Reason}");
            return Task.CompletedTask;
        }
    }
}

Solution

  • I'm stupid enough not to update my global variables in my main file:

    private readonly LavaNode _lavaNode; // OLD
    private readonly LavaNode<XLavaPlayer> _lavaNode; // NEW
    
    _lavaNode = _services.GetRequiredService<LavaNode>(); // OLD
    _lavaNode = _services.GetRequiredService<LavaNode<XLavaPlayer>>(); // NEW
    

    And finally ServiceCollection:

    .AddSingleton<LavaNode>() // OLD
    .AddSingleton<LavaNode<XLavaPlayer>>() // NEW