Problem z pingowaniem

0
Dyzma napisał(a):

Swoja drogą po "uproszczeniu" nie powinien rzucać takim błędów.
Zastosuj uproszczenie i wrzuć ponownie cały kod.

Zastosowałem się do Twoich porad i:

  • przy załączaniu nie ma błędów
  • uproszczenie Ktos dalej nie działa, jest ten sam błąd
  • przy przejściu z okna logowania do okna strony głównej wyrzuca błąd przy public HomePage() "System.InvalidOperationException: „Wątkiem wywołującym musi być STA, ponieważ wiele składników interfejsów użytkownika go wymaga.”

Spotkałem się z nim i jako pierwsze to pamiętam, że trzeba było gdzieś dodać [STAThread] lub trzeba było edytować w jakiś sposób kod, ale dokładnie nie wiem.

Cały kod:

namespace launchek
{
    /// <summary>
    /// Logika interakcji dla klasy HomePage.xaml
    /// </summary>
    /// 
    public partial class HomePage : Page
    {
        public HomePage() 
        {
            InitializeComponent();
        }

        MProfileInfo[] versions;
        MSession session;
        GamePatch Patcher;

        private void Window_Loaded(object sender, EventArgs e)
        {
            Task.Run(() =>
            {
                HomePage hmp = new HomePage();
                MineStat ms = new MineStat("mc.skkf.net", 25565);
                if (hmp.serverstatus2.Dispatcher.CheckAccess())
                    hmp.serverstatus2.Dispatcher.Invoke(new Action(() => hmp.serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Offline"));
                else
                    hmp.serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Offline";
            });

            string RootPath = Environment.GetEnvironmentVariable("appdata") + "\\SkyCraft";

            Minecraft.init(RootPath);
            versions = MProfileInfo.GetProfiles();
            Versions.Items.Clear();
            foreach (var item in versions)
            {
                Versions.Items.Add(item.Name);
            }
            Patcher = new GamePatch(session);

        }

        private void Play_Click_Btn(object sender, RoutedEventArgs e)
        {

        }

        private void GameFolder_Click_Btn(object sender, RoutedEventArgs e)
        {
            Patcher.OpenFolder();
        }
    }
}
0

Pozmieniałeś kod względem tego co wrzuciłem.
Znowu wrzuciłeś:

HomePage hmp = new HomePage();

Do task'a
I tutaj:

                if (hmp.serverstatus2.Dispatcher.CheckAccess())
                    hmp.serverstatus2.Dispatcher.Invoke(new Action(() => hmp.serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Offline"));
                else
                    hmp.serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Offline";

Pozmieniałeś kolejność.

0
Dyzma napisał(a):

Pozmieniałeś kolejność.

Okej, teraz wszystko powinno być jak należy, ale jak zwykle "jest jakieś ale".
Wszystko ładnie się ładuje, ale gdy dochodzi do momentu pokazania danych to:

  • wyrzuca dalej ten sam błąd przy hmp.serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Offline"; "System.InvalidOperationException: „Wątek wywołujący nie może uzyskać dostępu do tego obiektu, ponieważ należy on do innego wątku.”"
  • mimo wszystko dalej kod nie działa, gdyż serwer jest online, a całość próbuje załadować informację o tym, że jest offline.
        private void Window_Loaded(object sender, EventArgs e)
        {
            HomePage hmp = new HomePage();
            Task.Run(() =>
            {
                MineStat ms = new MineStat("mc.skkf.net", 25565);
                if (hmp.serverstatus2.Dispatcher.CheckAccess())
                    hmp.serverstatus2.Dispatcher.Invoke(new Action(() => hmp.serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Offline"));
                else
                    hmp.serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Offline";
            });
1

Bo pozmieniałeś kolejność.
Ma być:

if (hmp.serverstatus2.Dispatcher.CheckAccess())
     hmp.serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Offline";
else
     hmp.serverstatus2.Dispatcher.Invoke((Action) (() => { hmp.serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Offline" } ));

A zrobiłeś odwrotnie.

0
Dyzma napisał(a):

Bo pozmieniałeś kolejność.
Ma być:

if (hmp.serverstatus2.Dispatcher.CheckAccess())
     hmp.serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Offline";
else
     hmp.serverstatus2.Dispatcher.Invoke((Action) (() => { hmp.serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Offline" } ));

A zrobiłeś odwrotnie.

Przepraszam, cały czas się patrzyłem na ten kod, ale jakoś mój mózg nie zarejestrował tego, że jest źle, mimo porad.

Teraz całość jest wstawiona poprawnie, nie ma żadnych błędów.
Jednak kod mimo wszystko nie działa, TextBox jak ma wpisany "Status" tak on pozostaje, nie wyświetla się już nic.

Kod:

        private void Window_Loaded(object sender, EventArgs e)
        {
            HomePage hmp = new HomePage();
            Task.Run(() =>
            {
                MineStat ms = new MineStat ("mc.skkf.net", 25565);
                if (hmp.serverstatus2.Dispatcher.CheckAccess())
                    hmp.serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Offline";
                else
                    hmp.serverstatus2.Dispatcher.Invoke(new Action(() => hmp.serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Offline"));
            });

Dane wyjściowe:
"Wątek 0x2dec zakończył działanie z kodem 0 (0x0).
Zgłoszony wyjątek: „System.Exception” w launchek.exe
Wątek 0x6090 zakończył działanie z kodem 0 (0x0).
Wątek 0x4664 zakończył działanie z kodem 0 (0x0).
Wątek 0x5424 zakończył działanie z kodem 0 (0x0).
Wątek 0x5b0c zakończył działanie z kodem 0 (0x0).
Wątek 0x4888 zakończył działanie z kodem 0 (0x0)."

3

I teraz zaczyna się proces debugowania. ;)
Sprawdź czy ten kawałek kodu od ldilley od pobierania tych danych coś sensownego zwraca.
Żeby mieć pewność, że to nie z jego powodu są problemy. Albo z powodu serwera.
Kod 0 (0x0) to dobry znak, oznacza brak błędu.
I pamiętaj, że te dane będą uzupełniać się jak zostaną pobrane, nie wiem ile to trwało, ale będzie trwać tyle samo,
tylko, że nie będzie blokować wątku interfejsu aplikacji.

2

W twoim przypadku nie musisz używać dispatchera.
Sprawdź czy biblioteka działa poprawnie na jakimś działającym serwerze.

Ja zrobiłbym coś takiego.

private async void Window_Loaded(object sender, RoutedEventArgs e)
{
    await UpdateMineStatsAsync();
}

// tutaj działasz w wątku UI
private async Task UpdateMineStatsAsync()
{
    var mineStats = await GetMineStatsAsync();
    if (mineStats == null || !mineStats.ServerUp)
    {
        // labelek to akurat moja kontrolka :)
        labelek.Content = "Offline";
    }
    else
    {
        labelek.Content = mineStats.CurrentPlayers.ToString();
    }
}

// tutaj tworzysz nowy wątek (za pomocą task run) więc w nim nie możesz aktualizować wątku UI
private Task<MineStat> GetMineStatsTaskAsync()
{
    return Task.Run(() =>
    {
        try
        {
            return new MineStat("mc.skkf.net", 25565);
        }
        catch (Exception)
        {
            // coś nie działa (możesz zapisać log, ew. zwrócić coś innego)
            return null;
        }
    });
}

@Dyzma
Gdy użyjesz taska w ten sposób (nie używając await) zostanie on uruchomiony jako 'ffire and forget' tzn. nie dostaniesz żadnego exception z wewnątrz.

0

@Dyzma: Patrzyłem, ale nic się nie dzieje. Zostawiłem aplikację otwartą na dobrą chwilę i nic.
Teoretycznie wszystko działa.

@Sunnyline2 chcę sprawdzić kod, ale występuje błąd przy Window_Loaded:
await UpdateMineStatsAsync(); "Błąd CS4033 Operatora „await” można używać tylko wewnątrz metody asynchronicznej. Rozważ możliwość oznaczenia tej metody za pomocą modyfikatora „async” i zmiany zwracanego przez nią typu na „Task”."

Kod:

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            await UpdateMineStatsAsync();

            string RootPath = Environment.GetEnvironmentVariable("appdata") + "\\SkyCraft";

            Minecraft.init(RootPath);
            versions = MProfileInfo.GetProfiles();
            Versions.Items.Clear();
            foreach (var item in versions)
            {
                Versions.Items.Add(item.Name);
            }
            Patcher = new GamePatch(session);

        }
        // tutaj działasz w wątku UI
        private async Task UpdateMineStatsAsync()
        {
            var mineStats = await GetMineStatsTaskAsync();
            if (mineStats == null || !mineStats.ServerUp)
            {
                // labelek to akurat moja kontrolka :)
                serverstatus2.Text = "Offline";
            }
            else
            {
                serverstatus2.Text = mineStats.CurrentPlayers.ToString();
            }
        }

        // tutaj tworzysz nowy wątek (za pomocą task run) więc w nim nie możesz aktualizować wątku UI
        private Task<MineStat> GetMineStatsTaskAsync()
        {
            return Task.Run(() =>
            {
                try
                {
                    return new MineStat("mc.skkf.net", 25565);
                }
                catch (Exception)
                {
                    // coś nie działa (możesz zapisać log, ew. zwrócić coś innego)
                    return null;
                }
            });
        }
0

A jak debugujesz i wchodzisz do klasy MineStat to co tam się dzieje? ;)
Wszystko działa i zwraca wszystkie dane?

0
Dyzma napisał(a):

A jak debugujesz i wchodzisz do klasy MineStat to co tam się dzieje? ;)
Wszystko działa i zwraca wszystkie dane?

Wszystko działa, ale nic nie pokazuje.

0

Zatrzymaj się w debug'u po linijce:

MineStat ms = new MineStat("mc.skkf.net", 25565);

Najedź myszką na "ms", żeby pokazało pola, zrób screena i wrzuć.

0
Dyzma napisał(a):

Zatrzymaj się w debug'u po linijce:

MineStat ms = new MineStat("mc.skkf.net", 25565);

Najedź myszką na "ms", żeby pokazało pola, zrób screena i wrzuć.

Szczerze mówiąc nie za bardzo rozumiem o co chodzi.
Poniżej screen jaki udało mi się zrobić.
Nie wiem jak sprawdzić kiedy debugowanie dochodzi do momentu tej linijki.

title

1

Właśnie dlatego poprosiłem o screen, żeby mieć pewność, że wiesz jak to sprawdzić. ;)
Nie możesz zakładać, że "wszystko działa", jak nie jesteś w stanie sprawdzić wewnętrznej funkcjonalności.
To, że nie rzuciło błędem, nie znaczy, że działa.
Generalnie wciśnięcie klawisza F9 ustawia tzw. breakpoint, czyli punkt, w którym program się zatrzyma podczas debugowania.
Ustaw na tej linijce:

MineStat ms = new MineStat("mc.skkf.net", 25565);

I naciśnij F10, czyli przejście do kolejnej linii (w skrócie, po więcej zwróć się do google), żeby kod się wykonał.
A następnie jak najedziesz w Visual Studio na zmienną "ms" będziesz mógł podejrzeć, co jest w środku.
Generalnie bez umiejętności debugowania daleko nie zajdziesz, jest masa materiałów w internecie.
Proponuję zacząć od youtube i wpisania "Visual Studio C# debugging". ;)

1

To System.Exception to pewnie jest tak naprawdę SocketException: "A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond 104.18.54.100:25565", przynajmniej ja taki błąd dostaję próbując ten serwer, który tutaj masz podany. (po ludzku mówiąc: serwer nie działa) Biblioteka to przechwytuje i oznacza, że nie działa, ale w logach będziesz miał.

Tylko zanim to wyskoczy musi minąć timeout - u mnie jest to w okolicach 40 sekund (co jest swoją drogą ciekawe, bo chyba ta biblioteka ma ustalone standardowo na 5 sekund).

0

@Dyzma:
Screeny wrzucam na dole, ale nie mam pojęcia o co chodzi. Sprawdziłem to na 3 serwerach, na każdym pokazuje, że jest nieaktywny.
Sprawdziłem na debugowaniu oraz dodałem log i faktycznie przerzuca mnie cały czas na offline.
Dodatkowo napis się nie zmienia, całość przechodzi normalnie, ale nie wiem w którym momencie dzieje się to, że tekst nie zostaje zamieniony na chociażby "Offline".
title
title

@Ktos
Ogółem to sprawdzałem 3 serwery i każdy według kodu jest offline co jest nieprawdą. Sam na nie wszedłem, żeby się upewnić i nie ma takiej możliwości.

0

Czytaj proszę w całości to co piszę:

A następnie jak najedziesz w Visual Studio na zmienną "ms" będziesz mógł podejrzeć, co jest w środku.

title
Jak dla mnie kod z MineStat nie działa.
Zrób debug klasy MineStat. Ewentualnie poczytaj o TcpClient w C#, żeby wiedzieć co debugujesz.
I będziesz wiedział co i dlaczego nie działa. ;)

1

Problem polega na tym, że ten kod jest stary, a Minecraft się rozwija (cudem). https://github.com/ldilley/minestat/issues/19#issuecomment-449702779

0

@Dyzma:
To samo pokazało się na dole screena, więc uznałem, że nie mam po co najeżdżać skoro ta sama treść jest pokazana w oknie na dole.
Będę musiał spróbować to zrobić, jeszcze nie wiem jak, ale będę musiał.
Jedyną rzeczą, która mnie nurtuje to czemu napis się nie zmienia mimo, że wykrywa serwer offline.

@Ktos
To tego akurat nie widziałem, w każdym razie też ważna informacja.
Może po prostu poszukam jakiegoś innego kodu.

1
Daniel Urbaniec napisał(a):

@Dyzma:
To samo pokazało się na dole screena, więc uznałem, że nie mam po co najeżdżać skoro ta sama treść jest pokazana w oknie na dole.
Będę musiał spróbować to zrobić, jeszcze nie wiem jak, ale będę musiał.
Jedyną rzeczą, która mnie nurtuje to czemu napis się nie zmienia mimo, że wykrywa serwer offline.

@Ktos
To tego akurat nie widziałem, w każdym razie też ważna informacja.
Może po prostu poszukam jakiegoś innego kodu.

Jak ma coś wyświetlać skoro się wysypuje podczas tworzenia instancji mc stats?

0

@Sunnyline2:
No w sumie tak.
Chociaż dziwi mnie to, bo gdy na początku testowałem kod to działał tylko były ścinki o których mówiłem.
Znalazłem inny kod odnośnie konsoli, lecz jest problem z elementem var ping = JsonConvert.DeserializeObject<PingPayload>(json);
"Newtonsoft.Json.JsonReaderException: „Unexpected character encountered while parsing value: {. Path 'description', line 1, position 100.”"

using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using Newtonsoft.Json;

#if DEBUG
using System.Diagnostics;
#endif

namespace MCServerPing
{
    class ServerPing
    {
        private static readonly Dictionary<char, ConsoleColor> Colours = new Dictionary<char, ConsoleColor>
        {
             { '0', ConsoleColor.Black       },
             { '1', ConsoleColor.DarkBlue    },
             { '2', ConsoleColor.DarkGreen   },
             { '3', ConsoleColor.DarkCyan    },
             { '4', ConsoleColor.DarkRed     },
             { '5', ConsoleColor.DarkMagenta },
             { '6', ConsoleColor.Yellow      },
             { '7', ConsoleColor.Gray        },
             { '8', ConsoleColor.DarkGray    },
             { '9', ConsoleColor.Blue        },
             { 'a', ConsoleColor.Green       },
             { 'b', ConsoleColor.Cyan        },
             { 'c', ConsoleColor.Red         },
             { 'd', ConsoleColor.Magenta     },
             { 'e', ConsoleColor.Yellow      },
             { 'f', ConsoleColor.White       },
             { 'k', Console.ForegroundColor  },
             { 'l', Console.ForegroundColor  },
             { 'm', Console.ForegroundColor  },
             { 'n', Console.ForegroundColor  },
             { 'o', Console.ForegroundColor  },
             { 'r', ConsoleColor.White       }
        };

        private static NetworkStream _stream;
        private static List<byte> _buffer;
        private static int _offset;

        private static void Main(string[] args)
        {
            Console.Title = "Minecraft Server Ping";

            var client = new TcpClient();
            var task = client.ConnectAsync("mojemc.pl", 25565);
            Console.WriteLine("Connecting to Minecraft server..");

            while (!task.IsCompleted)
            {
#if DEBUG
                Debug.WriteLine("Connecting..");
#endif
                Thread.Sleep(250);
            }

            if (!client.Connected)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Unable to connect to the server");
                Console.ResetColor();
                Console.ReadKey(true);
                Environment.Exit(1);
            }

            _buffer = new List<byte>();
            _stream = client.GetStream();
            Console.WriteLine("Sending status request");


            /*
             * Send a "Handshake" packet
             * http://wiki.vg/Server_List_Ping#Ping_Process
             */
            WriteVarInt(47);
            WriteString("localhost");
            WriteShort(25565);
            WriteVarInt(1);
            Flush(0);

            /*
             * Send a "Status Request" packet
             * http://wiki.vg/Server_List_Ping#Ping_Process
             */
            Flush(0);

            /*
             * If you are using a modded server then use a larger buffer to account, 
             * see link for explanation and a motd to HTML snippet
             * https://gist.github.com/csh/2480d14fbbb33b4bbae3#gistcomment-2672658
             */
            var buffer = new byte[Int16.MaxValue];
            // var buffer = new byte[4096];
            _stream.Read(buffer, 0, buffer.Length);

            try
            {
                var length = ReadVarInt(buffer);
                var packet = ReadVarInt(buffer);
                var jsonLength = ReadVarInt(buffer);
#if DEBUG
                Console.WriteLine("Received packet 0x{0} with a length of {1}", packet.ToString("X2"), length);
#endif
                var json = ReadString(buffer, jsonLength);
                var ping = JsonConvert.DeserializeObject<PingPayload>(json);

                Console.WriteLine("Software: {0}", ping.Version.Name);
                Console.WriteLine("Protocol: {0}", ping.Version.Protocol);
                Console.WriteLine("Players Online: {0}/{1}", ping.Players.Online, ping.Players.Max);
                WriteMotd(ping);

                Console.ReadKey(true);
            }
            catch (IOException ex)
            {
                /*
                 * If an IOException is thrown then the server didn't 
                 * send us a VarInt or sent us an invalid one.
                 */
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Unable to read packet length from server,");
                Console.WriteLine("are you sure it's a Minecraft server?");
#if DEBUG
                Console.WriteLine("Here are the details:");
                Console.WriteLine(ex.ToString());
#endif
                Console.ResetColor();
            }
        }

        private static void WriteMotd(PingPayload ping)
        {
            Console.Write("Motd: ");
            var chars = ping.Motd.ToCharArray();
            for (var i = 0; i < ping.Motd.Length; i++)
            {
                try
                {
                    if (chars[i] == '\u00A7' && Colours.ContainsKey(chars[i + 1]))
                    {
                        Console.ForegroundColor = Colours[chars[i + 1]];
                        continue;
                    }
                    if (chars[i - 1] == '\u00A7' && Colours.ContainsKey(chars[i]))
                    {
                        continue;
                    }
                }
                catch (IndexOutOfRangeException)
                {
                    // End of string
                }
                Console.Write(chars[i]);
            }
            Console.WriteLine();
            Console.ResetColor();
        }

        #region Read/Write methods
        internal static byte ReadByte(byte[] buffer)
        {
            var b = buffer[_offset];
            _offset += 1;
            return b;
        }

        internal static byte[] Read(byte[] buffer, int length)
        {
            var data = new byte[length];
            Array.Copy(buffer, _offset, data, 0, length);
            _offset += length;
            return data;
        }

        internal static int ReadVarInt(byte[] buffer)
        {
            var value = 0;
            var size = 0;
            int b;
            while (((b = ReadByte(buffer)) & 0x80) == 0x80)
            {
                value |= (b & 0x7F) << (size++ * 7);
                if (size > 5)
                {
                    throw new IOException("This VarInt is an imposter!");
                }
            }
            return value | ((b & 0x7F) << (size * 7));
        }

        internal static string ReadString(byte[] buffer, int length)
        {
            var data = Read(buffer, length);
            return Encoding.UTF8.GetString(data);
        }

        internal static void WriteVarInt(int value)
        {
            while ((value & 128) != 0)
            {
                _buffer.Add((byte)(value & 127 | 128));
                value = (int)((uint)value) >> 7;
            }
            _buffer.Add((byte)value);
        }

        internal static void WriteShort(short value)
        {
            _buffer.AddRange(BitConverter.GetBytes(value));
        }

        internal static void WriteString(string data)
        {
            var buffer = Encoding.UTF8.GetBytes(data);
            WriteVarInt(buffer.Length);
            _buffer.AddRange(buffer);
        }

        internal static void Write(byte b)
        {
            _stream.WriteByte(b);
        }

        internal static void Flush(int id = -1)
        {
            var buffer = _buffer.ToArray();
            _buffer.Clear();

            var add = 0;
            var packetData = new[] { (byte)0x00 };
            if (id >= 0)
            {
                WriteVarInt(id);
                packetData = _buffer.ToArray();
                add = packetData.Length;
                _buffer.Clear();
            }

            WriteVarInt(buffer.Length + add);
            var bufferLength = _buffer.ToArray();
            _buffer.Clear();

            _stream.Write(bufferLength, 0, bufferLength.Length);
            _stream.Write(packetData, 0, packetData.Length);
            _stream.Write(buffer, 0, buffer.Length);
        }
        #endregion
    }

    #region Server ping 
    /// <summary>
    /// C# represenation of the following JSON file
    /// https://gist.github.com/thinkofdeath/6927216
    /// </summary>
    class PingPayload
    {
        /// <summary>
        /// Protocol that the server is using and the given name
        /// </summary>
        [JsonProperty(PropertyName = "version")]
        public VersionPayload Version { get; set; }

        [JsonProperty(PropertyName = "players")]
        public PlayersPayload Players { get; set; }

        [JsonProperty(PropertyName = "description")]
        public string Motd { get; set; }

        /// <summary>
        /// Server icon, important to note that it's encoded in base 64
        /// </summary>
        [JsonProperty(PropertyName = "favicon")]
        public string Icon { get; set; }
    }

    class VersionPayload
    {
        [JsonProperty(PropertyName = "protocol")]
        public int Protocol { get; set; }

        [JsonProperty(PropertyName = "name")]
        public string Name { get; set; }
    }

    class PlayersPayload
    {
        [JsonProperty(PropertyName = "max")]
        public int Max { get; set; }

        [JsonProperty(PropertyName = "online")]
        public int Online { get; set; }

        [JsonProperty(PropertyName = "sample")]
        public List<Player> Sample { get; set; }
    }

    class Player
    {
        [JsonProperty(PropertyName = "name")]
        public string Name { get; set; }

        [JsonProperty(PropertyName = "id")]
        public string Id { get; set; }
    }
    #endregion
}
0

W komentarzach na GitHub pod tym projektem już dawno ktoś narzekał, że nie działa na niektórych serwerach.
A nie działa, bo struktura json'a się zmieniła.
Poprawiłem:

using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using Newtonsoft.Json;
using System.Text.RegularExpressions;

#if DEBUG
using System.Diagnostics;
#endif

namespace MCServerPing
{
    class ServerPing
    {
        private static readonly Dictionary<char, ConsoleColor> Colours = new Dictionary<char, ConsoleColor>
        {
             { '0', ConsoleColor.Black       },
             { '1', ConsoleColor.DarkBlue    },
             { '2', ConsoleColor.DarkGreen   },
             { '3', ConsoleColor.DarkCyan    },
             { '4', ConsoleColor.DarkRed     },
             { '5', ConsoleColor.DarkMagenta },
             { '6', ConsoleColor.Yellow      },
             { '7', ConsoleColor.Gray        },
             { '8', ConsoleColor.DarkGray    },
             { '9', ConsoleColor.Blue        },
             { 'a', ConsoleColor.Green       },
             { 'b', ConsoleColor.Cyan        },
             { 'c', ConsoleColor.Red         },
             { 'd', ConsoleColor.Magenta     },
             { 'e', ConsoleColor.Yellow      },
             { 'f', ConsoleColor.White       },
             { 'k', Console.ForegroundColor  },
             { 'l', Console.ForegroundColor  },
             { 'm', Console.ForegroundColor  },
             { 'n', Console.ForegroundColor  },
             { 'o', Console.ForegroundColor  },
             { 'r', ConsoleColor.White       }
        };

        private static NetworkStream _stream;
        private static List<byte> _buffer;
        private static int _offset;

        private static void Main(string[] args)
        {
            Console.Title = "Minecraft Server Ping";

            var client = new TcpClient();
            var task = client.ConnectAsync("mojemc.pl", 25565);
            Console.WriteLine("Connecting to Minecraft server..");

            while (!task.IsCompleted)
            {
#if DEBUG
                Debug.WriteLine("Connecting..");
#endif
                Thread.Sleep(250);
            }

            if (!client.Connected)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Unable to connect to the server");
                Console.ResetColor();
                Console.ReadKey(true);
                Environment.Exit(1);
            }

            _buffer = new List<byte>();
            _stream = client.GetStream();
            Console.WriteLine("Sending status request");

            /*
             * Send a "Handshake" packet
             * http://wiki.vg/Server_List_Ping#Ping_Process
             */
            WriteVarInt(47);
            WriteString("localhost");
            WriteShort(25565);
            WriteVarInt(1);
            Flush(0);

            /*
             * Send a "Status Request" packet
             * http://wiki.vg/Server_List_Ping#Ping_Process
             */
            Flush(0);

            /*
             * If you are using a modded server then use a larger buffer to account, 
             * see link for explanation and a motd to HTML snippet
             * https://gist.github.com/csh/2480d14fbbb33b4bbae3#gistcomment-2672658
             */
            var buffer = new byte[Int16.MaxValue];
            // var buffer = new byte[4096];
            _stream.Read(buffer, 0, buffer.Length);

            try
            {
                var length = ReadVarInt(buffer);
                var packet = ReadVarInt(buffer);
                var jsonLength = ReadVarInt(buffer);
#if DEBUG
                Console.WriteLine("Received packet 0x{0} with a length of {1}", packet.ToString("X2"), length);
#endif
                var json = ReadString(buffer, jsonLength);
                var ping = JsonConvert.DeserializeObject<PingPayload>(json);

                Console.WriteLine("Software: {0}", ping.Version.Name);
                Console.WriteLine("Protocol: {0}", ping.Version.Protocol);
                Console.WriteLine("Players Online: {0}/{1}", ping.Players.Online, ping.Players.Max);
                WriteMotd(ping);

                Console.ReadKey(true);
            }
            catch (IOException ex)
            {
                /*
                 * If an IOException is thrown then the server didn't 
                 * send us a VarInt or sent us an invalid one.
                 */
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Unable to read packet length from server,");
                Console.WriteLine("are you sure it's a Minecraft server?");
#if DEBUG
                Console.WriteLine("Here are the details:");
                Console.WriteLine(ex.ToString());
#endif
                Console.ResetColor();
            }
        }

        private static void WriteMotd(PingPayload ping)
        {
            Console.Write("Motd: ");
            var chars = ping.Motd.Text.ToCharArray();
            for (var i = 0; i < ping.Motd.Text.Length; i++)
            {
                try
                {
                    if (chars[i] == '\u00A7' && Colours.ContainsKey(chars[i + 1]))
                    {
                        Console.ForegroundColor = Colours[chars[i + 1]];
                        continue;
                    }
                    if (chars[i - 1] == '\u00A7' && Colours.ContainsKey(chars[i]))
                    {
                        continue;
                    }
                }
                catch (IndexOutOfRangeException)
                {
                    // End of string
                }
                Console.Write(chars[i]);
            }
            Console.WriteLine();
            Console.ResetColor();
        }

        #region Read/Write methods
        internal static byte ReadByte(byte[] buffer)
        {
            var b = buffer[_offset];
            _offset += 1;
            return b;
        }

        internal static byte[] Read(byte[] buffer, int length)
        {
            var data = new byte[length];
            Array.Copy(buffer, _offset, data, 0, length);
            _offset += length;
            return data;
        }

        internal static int ReadVarInt(byte[] buffer)
        {
            var value = 0;
            var size = 0;
            int b;
            while (((b = ReadByte(buffer)) & 0x80) == 0x80)
            {
                value |= (b & 0x7F) << (size++ * 7);
                if (size > 5)
                {
                    throw new IOException("This VarInt is an imposter!");
                }
            }
            return value | ((b & 0x7F) << (size * 7));
        }

        internal static string ReadString(byte[] buffer, int length)
        {
            var data = Read(buffer, length);
            return Encoding.UTF8.GetString(data);
        }

        internal static void WriteVarInt(int value)
        {
            while ((value & 128) != 0)
            {
                _buffer.Add((byte)(value & 127 | 128));
                value = (int)((uint)value) >> 7;
            }
            _buffer.Add((byte)value);
        }

        internal static void WriteShort(short value)
        {
            var bytes = BitConverter.GetBytes(value);
            if (BitConverter.IsLittleEndian)
                Array.Reverse(bytes);
            _buffer.AddRange(BitConverter.GetBytes(value));
        }

        internal static void WriteString(string data)
        {
            var buffer = Encoding.UTF8.GetBytes(data);
            WriteVarInt(buffer.Length);
            _buffer.AddRange(buffer);
        }

        internal static void Write(byte b)
        {
            _stream.WriteByte(b);
        }

        internal static void Flush(int id = -1)
        {
            var buffer = _buffer.ToArray();
            _buffer.Clear();

            var add = 0;
            var packetData = new[] { (byte)0x00 };
            if (id >= 0)
            {
                WriteVarInt(id);
                packetData = _buffer.ToArray();
                add = packetData.Length;
                _buffer.Clear();
            }

            WriteVarInt(buffer.Length + add);
            var bufferLength = _buffer.ToArray();
            _buffer.Clear();

            _stream.Write(bufferLength, 0, bufferLength.Length);
            _stream.Write(packetData, 0, packetData.Length);
            _stream.Write(buffer, 0, buffer.Length);
        }
        #endregion
    }

    #region Server ping 
    /// <summary>
    /// C# represenation of the following JSON file
    /// https://gist.github.com/thinkofdeath/6927216
    /// </summary>
    class PingPayload
    {
        /// <summary>
        /// Protocol that the server is using and the given name
        /// </summary>
        [JsonProperty(PropertyName = "version")]
        public VersionPayload Version { get; set; }

        [JsonProperty(PropertyName = "players")]
        public PlayersPayload Players { get; set; }

        [JsonProperty(PropertyName = "description")]
        public DescriptionPayload Motd { get; set; }

        /// <summary>
        /// Server icon, important to note that it's encoded in base 64
        /// </summary>
        [JsonProperty(PropertyName = "favicon")]
        public string Icon { get; set; }
    }

    class DescriptionPayload
    {
        [JsonProperty(PropertyName = "text")]
        public string Text { get; set; }
    }

    class VersionPayload
    {
        [JsonProperty(PropertyName = "protocol")]
        public int Protocol { get; set; }

        [JsonProperty(PropertyName = "name")]
        public string Name { get; set; }
    }

    class PlayersPayload
    {
        [JsonProperty(PropertyName = "max")]
        public int Max { get; set; }

        [JsonProperty(PropertyName = "online")]
        public int Online { get; set; }

        [JsonProperty(PropertyName = "sample")]
        public List<Player> Sample { get; set; }
    }

    class Player
    {
        [JsonProperty(PropertyName = "name")]
        public string Name { get; set; }

        [JsonProperty(PropertyName = "id")]
        public string Id { get; set; }
    }
    #endregion
}

Czyli różne serwery mogą mieć różne struktury.
Zatem może na niektórych dalej się wysypywać na deserializacji json'a.

0

@Dyzma:
@Sunnyline2
@Ktos
@obscurity

Powiem tak - kod przekształciłem na swoje potrzeby, wrzuciłem go do ```HomePage : Page``.
Całość działa, lecz zauważyłem mały problem:

  • serwery typu mc.skkf.net , hypixel.net , pvpstar.pl nie funkcjonują, po prostu następuje loop w kodzie pomiędzy while (!task.IsCompleted) a Thread.Sleep(250);.
    Debugowałem całość, pokazuje jakby brak połączenia z serwerem, ale powstaje loop (?). Dodatkowo, gdy nie wpiszę IP tylko np. "xd" to całość ładnie zakończy działanie, pokaże informację o serwerze "Offline" i wszystko zapisze w logach.

Tutaj daje mój edytowany kod:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using launchek.Core;
using log4net;
using System.Threading;
using Newtonsoft.Json;
using System.Net.Sockets;
using System.Diagnostics;
using System.IO;

namespace launchek
{
    /// <summary>
    /// Logika interakcji dla klasy HomePage.xaml
    /// </summary>
    /// 
    public partial class HomePage : Page
    {
        public static NetworkStream _stream;
        public static List<byte> _buffer;
        public static int _offset;
        private static ILog log = LogManager.GetLogger("HomePage");

        public HomePage() 
        {
            InitializeComponent();
        }

        MProfileInfo[] versions;
        MSession session;
        GamePatch Patcher;

        private void Window_Loaded(object sender, EventArgs e)
        {

            var client = new TcpClient();
            var task = client.ConnectAsync("hypixel.net", 25565);
            log.Info("Connecting to Minecraft server..");
            Task.Run(() =>
            {
                while (!task.IsCompleted)
                {
                    log.Info("Connecting..");
                    Thread.Sleep(250);
                }
                if (!client.Connected)
                {
                    serverstatus2.Dispatcher.Invoke(new Action(() => serverstatus2.Text = "Offline"));
                    log.Info("Offline");
                    return;
                }

                _buffer = new List<byte>();
                _stream = client.GetStream();
                log.Info("Sending status request");

                WriteVarInt(47);
                WriteString("localhost");
                WriteShort(25565);
                WriteVarInt(1);
                Flush(0);
                Flush(0);

                var buffer = new byte[Int16.MaxValue];
                _stream.Read(buffer, 0, buffer.Length);

                try
                {
                    var length = ReadVarInt(buffer);
                    var packet = ReadVarInt(buffer);
                    var jsonLength = ReadVarInt(buffer);
                    var json = ReadString(buffer, jsonLength);
                    var ping = JsonConvert.DeserializeObject<PingPayload>(json);

                    serverstatus2.Dispatcher.Invoke(new Action(() => serverstatus2.Text = ping.Players.Online + "/" + ping.Players.Max ));
                }
                catch (IOException ex)
                {
                    log.Info("Unable to read packet length from server,");
                    log.Info("are you sure it's a Minecraft server?");
#if DEBUG
                    log.Info("Here are the details:");
                    log.Info(ex.ToString());
#endif
                }
            });




            string RootPath = Environment.GetEnvironmentVariable("appdata") + "\\SkyCraft";

            Minecraft.init(RootPath);
            versions = MProfileInfo.GetProfiles();
            Versions.Items.Clear();
            foreach (var item in versions)
            {
                Versions.Items.Add(item.Name);
            }
            Patcher = new GamePatch(session);

        }

        private void Play_Click_Btn(object sender, RoutedEventArgs e)
        {

        }

        private void GameFolder_Click_Btn(object sender, RoutedEventArgs e)
        {
            Patcher.OpenFolder();
        }







    #region Read/Write methods
    internal static byte ReadByte(byte[] buffer)
    {
        var b = buffer[_offset];
        _offset += 1;
        return b;
    }

    internal static byte[] Read(byte[] buffer, int length)
    {
        var data = new byte[length];
        Array.Copy(buffer, _offset, data, 0, length);
        _offset += length;
        return data;
    }

    internal static int ReadVarInt(byte[] buffer)
    {
        var value = 0;
        var size = 0;
        int b;
        while (((b = ReadByte(buffer)) & 0x80) == 0x80)
        {
            value |= (b & 0x7F) << (size++ * 7);
            if (size > 5)
            {
                throw new IOException("This VarInt is an imposter!");
            }
        }
        return value | ((b & 0x7F) << (size * 7));
    }

    internal static string ReadString(byte[] buffer, int length)
    {
        var data = Read(buffer, length);
        return Encoding.UTF8.GetString(data);
    }

    internal static void WriteVarInt(int value)
    {
        while ((value & 128) != 0)
        {
            _buffer.Add((byte)(value & 127 | 128));
            value = (int)((uint)value) >> 7;
        }
        _buffer.Add((byte)value);
    }

    internal static void WriteShort(short value)
    {
        var bytes = BitConverter.GetBytes(value);
        if (BitConverter.IsLittleEndian)
            Array.Reverse(bytes);
        _buffer.AddRange(BitConverter.GetBytes(value));
    }

    internal static void WriteString(string data)
    {
        var buffer = Encoding.UTF8.GetBytes(data);
        WriteVarInt(buffer.Length);
        _buffer.AddRange(buffer);
    }

    internal static void Write(byte b)
    {
        _stream.WriteByte(b);
    }

    internal static void Flush(int id = -1)
    {
        var buffer = _buffer.ToArray();
        _buffer.Clear();

        var add = 0;
        var packetData = new[] { (byte)0x00 };
        if (id >= 0)
        {
            WriteVarInt(id);
            packetData = _buffer.ToArray();
            add = packetData.Length;
            _buffer.Clear();
        }

        WriteVarInt(buffer.Length + add);
        var bufferLength = _buffer.ToArray();
        _buffer.Clear();

        _stream.Write(bufferLength, 0, bufferLength.Length);
        _stream.Write(packetData, 0, packetData.Length);
        _stream.Write(buffer, 0, buffer.Length);
    }
    #endregion
}

#region Server ping 
class PingPayload
    {
        /// <summary>
        /// Protocol that the server is using and the given name
        /// </summary>
        [JsonProperty(PropertyName = "version")]
        public VersionPayload Version { get; set; }

        [JsonProperty(PropertyName = "players")]
        public PlayersPayload Players { get; set; }

        [JsonProperty(PropertyName = "description")]
        public DescriptionPayload Motd { get; set; }

        /// <summary>
        /// Server icon, important to note that it's encoded in base 64
        /// </summary>
        [JsonProperty(PropertyName = "favicon")]
        public string Icon { get; set; }
    }

    class DescriptionPayload
    {
        [JsonProperty(PropertyName = "text")]
        public string Text { get; set; }
    }

    class VersionPayload
    {
        [JsonProperty(PropertyName = "protocol")]
        public int Protocol { get; set; }

        [JsonProperty(PropertyName = "name")]
        public string Name { get; set; }
    }

    class PlayersPayload
    {
        [JsonProperty(PropertyName = "max")]
        public int Max { get; set; }

        [JsonProperty(PropertyName = "online")]
        public int Online { get; set; }

        [JsonProperty(PropertyName = "sample")]
        public List<Player> Sample { get; set; }
    }

    class Player
    {
        [JsonProperty(PropertyName = "name")]
        public string Name { get; set; }

        [JsonProperty(PropertyName = "id")]
        public string Id { get; set; }
    }
    #endregion
}
1

Bo tam jest po prostu pętla.
Na pierwszej stronie tego wątku zwróciłem uwagę, że nie ma timeout'u dla TcpClient w przypadku "Connect".
Jeśli zrobisz po prostu Connect, to będzie czekał na odpowiedź jakąkolwiek, czasem bardzo długo.
I taka sytuacja ma miejsce właśnie w tym przypadku.
Jak wpiszesz cokolwiek zamiast adresu to przechodzi dalej, bo od razu ma odpowiedź, że nie da się ustanowić połączenia.
Również na pierwszej stronie napisałem kawałek kodu, który pozwala "dodać" timeout dla połączenia.
A dlaczego niektóre serwery odpowiadają, a niektóre nie - nie wiem.
Pewnie trzeba by przysiąść i się dowiedzieć jak to działa. ;)

0
Dyzma napisał(a):

Bo tam jest po prostu pętla.
Na pierwszej stronie tego wątku zwróciłem uwagę, że nie ma timeout'u dla TcpClient w przypadku "Connect".
Jeśli zrobisz po prostu Connect, to będzie czekał na odpowiedź jakąkolwiek, czasem bardzo długo.
I taka sytuacja ma miejsce właśnie w tym przypadku.
Jak wpiszesz cokolwiek zamiast adresu to przechodzi dalej, bo od razu ma odpowiedź, że nie da się ustanowić połączenia.
Również na pierwszej stronie napisałem kawałek kodu, który pozwala "dodać" timeout dla połączenia.
A dlaczego niektóre serwery odpowiadają, a niektóre nie - nie wiem.
Pewnie trzeba by przysiąść i się dowiedzieć jak to działa. ;)

Okej, w każdym razie dziękuję każdemu za pomoc i myślę, że temat jest do zamknięcia.
Z resztą spróbuję uporać się sam. Jeszcze raz - dzięki wielkie :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1