TeamSpeak3QueryApi icon indicating copy to clipboard operation
TeamSpeak3QueryApi copied to clipboard

Feature: Event on connection lost

Open dedmen opened this issue 5 years ago • 5 comments

I have the issue that my service monitoring my TS server is apparently loosing Query connection after hours/days.

Would be good to have a event to notify me of that, such that I can trigger a reconnect.

I think firing a event from here would work?: https://github.com/nikeee/TeamSpeak3QueryAPI/blob/master/src/TeamSpeak3QueryApi/QueryClient.cs#L394

dedmen avatar Jan 29 '20 13:01 dedmen

why not make a loop that checks if the bool function IsConnected() of TeamSpeakClient.Client is false?

realbadidas avatar Mar 18 '20 20:03 realbadidas

why not make a loop that checks if the bool function IsConnected() of TeamSpeakClient.Client is false?

I tried that at first, didn't work. Right now my workaround is to simply send "version" command on a 4 minute timer, to make sure it doesn't idle kick me. That has ran flawlessly for weeks now.

dedmen avatar Mar 19 '20 09:03 dedmen

Hey @dedmen ,

there is a much smarter solution by just added the event. You just need to add your QueryClient.cs like this.

Adding the Eventhandler

    public class QueryClient : IDisposable
    {
        public EventHandler OnConnectionLost;

        /// <summary>Gets the remote host of the Query API client.</summary>
        /// <returns>The remote host of the Query API client.</returns>
        public string Host { get; }

Adding the firing method

After that you need to create the virtual method the fire the event. I´ve added this code before the constructors but it doesen´t matter where you add it.

        #region Events
        public virtual void EventConnectionLost(EventArgs e)
        {
            OnConnectionLost?.Invoke(this, e);
        }
        #endregion

Firing the method if the connection is lost

Now you just need to fire the method if the client lost the connection. That on line 407. There you just type EventConnectionLost(EventArgs.Empty);. But to be sure here is the whole class of ResponseProcessingLoop

        private CancellationTokenSource ResponseProcessingLoop()
        {
            var cts = _cts = new CancellationTokenSource();
            Task.Run(async () =>
            {
                while (!cts.Token.IsCancellationRequested)
                {
                    string line = null;
                    try
                    {
                        line = await _reader.ReadLineAsync().WithCancellation(cts.Token).ConfigureAwait(false);
                    }
                    catch (OperationCanceledException)
                    {
                        break;
                    }

                    Debug.WriteLine(line);

                    if (line == null)
                    {
                        cts.Cancel();
                        continue;
                    }

                    if (string.IsNullOrWhiteSpace(line))
                        continue;

                    var s = line.Trim();

                    if (s.StartsWith("error", StringComparison.OrdinalIgnoreCase))
                    {
                        Debug.Assert(_currentCommand != null);

                        var error = ParseError(s);
                        _currentCommand.Error = error;
                        InvokeResponse(_currentCommand);
                    }
                    else if (s.StartsWith("notify", StringComparison.OrdinalIgnoreCase))
                    {
                        s = s.Remove(0, "notify".Length);
                        var not = ParseNotification(s);
                        InvokeNotification(not);
                    }
                    else
                    {
                        Debug.Assert(_currentCommand != null);
                        _currentCommand.RawResponse = s;
                        _currentCommand.ResponseDictionary = ParseResponse(s);
                    }
                }

                IsConnected = false;
                EventConnectionLost(EventArgs.Empty);

            });
            return cts;
        }

That´s it. Now you can easy check for a lost connection without traffic waste 👍 . Now to be fair here is my example how I check the connection.

Example

_client.Client.OnConnectionLost += async (sender, e) =>
{
    Log.Error($"Instance {Instance.Alias}: Verbindung zum Server verloren...");
    Log.Error($"Instance {Instance.Alias}: Verbindungsversuch wird in {TIME_TO_RETRY} erneut versucht...");
    await Task.Delay(TIME_TO_RETRY);
    _ = Connect();
};

I hope you can work with this :)

lgund avatar Jul 02 '20 12:07 lgund

I hope you can work with this

Yeah thats exactly what I was requesting. Why not make a pull request and get it added to the library?

dedmen avatar Jul 06 '20 15:07 dedmen

I will make a pull request if I am finished with fixing all problems. There are also a problem if the user is banned. There will be no QueryExecaption casted. Currently in my project I added the whole source and not the package to develop live with the source. I just read this issue and create you fast fix 👍

lgund avatar Jul 06 '20 18:07 lgund