Problem z pingowaniem

Problem z pingowaniem
Daniel Urbaniec
  • Rejestracja:ponad 5 lat
  • Ostatnio:prawie 5 lat
  • Lokalizacja:Kraków
  • Postów:40
0

Witam.
Tworzę launcher do gry i mam problem z pingowaniem serwera.
Gdy uruchamiam aplikację wszystko ładuje się do momentu odczytania wiadomości z serwera, a dokładniej ilości graczy i czy serwer jest aktywny.
Launcher zawiesza się lub dłuższą chwilę ładuje dane i zostają one pokazane.
Wiadomo, że dla użytkownika takiej aplikacji jest to mega uciążliwe i chciałbym się pozbyć tego problemu.

Tutaj kod używany podczas ładowania strony głównej:

Kopiuj
        private void Window_Loaded(object sender, EventArgs e)
        {
            MineStat ms = new MineStat("mc.skkf.net", 25565);
            if (ms.ServerUp)
            {
            serverstatus2.Text = ms.CurrentPlayers;
            }
            else
                serverstatus2.Text = "Server is offline!";

        }

Tutaj kod na pingowanie serwera stworzony przez ldilley z GitHuba:

Kopiuj
using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Diagnostics;

namespace launchek.Core
{
    public class MineStat
    {
        const ushort dataSize = 512;  // this will hopefully suffice since the MotD should be <=59 characters
        const ushort numFields = 6;   // number of values expected from server
        const int defaultTimeout = 5; // default TCP timeout in seconds

        public string Address { get; set; }
        public ushort Port { get; set; }
        public int Timeout { get; set; }
        public string Motd { get; set; }
        public string Version { get; set; }
        public string CurrentPlayers { get; set; }
        public string MaximumPlayers { get; set; }
        public bool ServerUp { get; set; }
        public long Latency { get; set; }

        public MineStat(string address, ushort port, int timeout = defaultTimeout)
        {
            var rawServerData = new byte[dataSize];

            Address = address;
            Port = port;
            Timeout = timeout * 1000;   // milliseconds

            try
            {
                var stopWatch = new Stopwatch();
                var tcpclient = new TcpClient();
                tcpclient.ReceiveTimeout = Timeout;
                stopWatch.Start();
                tcpclient.Connect(address, port);
                stopWatch.Stop();
                Latency = stopWatch.ElapsedMilliseconds;
                var stream = tcpclient.GetStream();
                var payload = new byte[] { 0xFE, 0x01 };
                stream.Write(payload, 0, payload.Length);
                stream.Read(rawServerData, 0, dataSize);
                tcpclient.Close();
            }
            catch (Exception)
            {
                ServerUp = false;
                return;
            }

            if (rawServerData == null || rawServerData.Length == 0)
            {
                ServerUp = false;
            }
            else
            {
                var serverData = Encoding.Unicode.GetString(rawServerData).Split("\u0000\u0000\u0000".ToCharArray());
                if (serverData != null && serverData.Length >= numFields)
                {
                    ServerUp = true;
                    Version = serverData[2];
                    Motd = serverData[3];
                    CurrentPlayers = serverData[4];
                    MaximumPlayers = serverData[5];
                }
                else
                {
                    ServerUp = false;
                }
            }
        }

        #region Obsolete

        [Obsolete]
        public string GetAddress()
        {
            return Address;
        }

        [Obsolete]
        public void SetAddress(string address)
        {
            Address = address;
        }

        [Obsolete]
        public ushort GetPort()
        {
            return Port;
        }

        [Obsolete]
        public void SetPort(ushort port)
        {
            Port = port;
        }

        [Obsolete]
        public string GetMotd()
        {
            return Motd;
        }

        [Obsolete]
        public void SetMotd(string motd)
        {
            Motd = motd;
        }

        [Obsolete]
        public string GetVersion()
        {
            return Version;
        }

        [Obsolete]
        public void SetVersion(string version)
        {
            Version = version;
        }

        [Obsolete]
        public string GetCurrentPlayers()
        {
            return CurrentPlayers;
        }

        [Obsolete]
        public void SetCurrentPlayers(string currentPlayers)
        {
            CurrentPlayers = currentPlayers;
        }

        [Obsolete]
        public string GetMaximumPlayers()
        {
            return MaximumPlayers;
        }

        [Obsolete]
        public void SetMaximumPlayers(string maximumPlayers)
        {
            MaximumPlayers = maximumPlayers;
        }

        [Obsolete]
        public long GetLatency()
        {
            return Latency;
        }

        [Obsolete]
        public void SetLatency(long latency)
        {
            Latency = latency;
        }

        [Obsolete]
        public bool IsServerUp()
        {
            return ServerUp;
        }

        #endregion
    }
}

Dziękuję za wszelką pomoc!

edytowany 1x, ostatnio: cerrato
PerlMonk
Nie wiem czemu jak widzę ten temat w rubryce aktywności to widzę "Problemy z pingwinem".
Yukiteru Gromadzki
  • Rejestracja:ponad 6 lat
  • Ostatnio:ponad rok
  • Postów:192
1

Spróbuj utworzyć taką metodę

Kopiuj
        public static async Task LoadServerInfo()
        {
            await Task.Run(() =>
            {
                MineStat ms = new MineStat("mc.skkf.net", 25565);
                serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Server is offline!";
            });
        }

I w Window_Load zastąp tamten kod wywołaniem funkcji LoadServerInfo();
Uruchom i daj znać czy działa.

EDIT: Nie wiem czy java nie byłaby lepszym rozwiązaniem.

edytowany 1x, ostatnio: Yukiteru Gromadzki
Daniel Urbaniec
  • Rejestracja:ponad 5 lat
  • Ostatnio:prawie 5 lat
  • Lokalizacja:Kraków
  • Postów:40
0
Yukiteru Gromadzki napisał(a):

Spróbuj utworzyć taką metodę

Kopiuj
        public static async Task LoadServerInfo()
        {
            await Task.Run(() =>
            {
                MineStat ms = new MineStat("mc.skkf.net", 25565);
                serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Server is offline!";
            });
        }

I w Window_Load zastąp tamten kod wywołaniem funkcji LoadServerInfo();
Uruchom i daj znać czy działa.

EDIT: Nie wiem czy java nie byłaby lepszym rozwiązaniem.

Teoretycznie ldilley wrzucił również kod JS oraz Java, ale nie znam się na tym kompletnie + nie wiem czy jest możliwość dodania go w aplikacji tworzonej w C# WPF.

Co do Twojej metody:

  • dodałem odwołanie do obiektu
Kopiuj
HomePage hmp = new HomePage();
hmp.serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Server is offline!";
  • dostaję błędny komunikat "CS0123 C# Żadne z przeciążeń dla elementu „LoadServerInfo” nie pasuje do delegata „RoutedEventHandler”."
edytowany 1x, ostatnio: Daniel Urbaniec
Ktos
Moderator
  • Rejestracja:prawie 23 lata
  • Ostatnio:około 3 godziny
0

To LoadServerInfo powinieneś odpalać wewnątrz Window_Loaded, robisz tak?

Daniel Urbaniec
  • Rejestracja:ponad 5 lat
  • Ostatnio:prawie 5 lat
  • Lokalizacja:Kraków
  • Postów:40
0
Ktos napisał(a):

To LoadServerInfo powinieneś odpalać wewnątrz Window_Loaded, robisz tak?

Kopiuj
        private void Window_Loaded(object sender, EventArgs e)
        {
            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);
            }

            async Task LoadServerInfo()
            {
                await Task.Run(() =>
                {
                    MineStat ms = new MineStat("mc.skkf.net", 25565);
                    serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Server is offline!";
                });
            }
        }

Dokładnie tak to robię.
Nie ma żadnego rezultatu.
"Ostrzeżenie CS8321 Funkcja lokalna „LoadServerInfo” jest zadeklarowana, lecz nie jest nigdy używana"

edytowany 1x, ostatnio: Daniel Urbaniec
Ktos
Moderator
  • Rejestracja:prawie 23 lata
  • Ostatnio:około 3 godziny
1

Wklej ją na poziomie klasy i wewnątrz Window_Loaded uruchom (po prostu LoadServerInfo();), bo tak obecnie to tworzysz funkcję wewnętrzną i nie odpalasz.

Daniel Urbaniec
  • Rejestracja:ponad 5 lat
  • Ostatnio:prawie 5 lat
  • Lokalizacja:Kraków
  • Postów:40
0
Ktos napisał(a):

Wklej ją na poziomie klasy i wewnątrz Window_Loaded uruchom (po prostu LoadServerInfo();), bo tak obecnie to tworzysz funkcję wewnętrzną i nie odpalasz.

Zrobiłem to w następujący sposób:

Kopiuj
    public partial class HomePage : Page
    {
        public static async Task LoadServerInfo()
        {
            await Task.Run(() =>
            {
                MineStat ms = new MineStat("mc.skkf.net", 25565);
                HomePage hmp = new HomePage();
                hmp.serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Server is offline!";
            });
        }
        public HomePage()
        {
            InitializeComponent();

        }

        MProfileInfo[] versions;
        MSession session;
        GamePatch Patcher;

        private void Window_Loaded(object sender, EventArgs e)
        {
            LoadServerInfo();
        }

Całość nie zawiera błędów, jedynie ostrzeżenie odnośnie LoadServerInfo();:
"CS4014 Ponieważ to wywołanie nie jest oczekiwane, wykonywanie bieżącej metody będzie kontynuowane bez oczekiwania na ukończenie wywołania. Rozważ możliwość zastosowania operatora „await” do wyniku wywołania."
Jednak całość nadal nie działa.

edytowany 1x, ostatnio: Daniel Urbaniec
Yukiteru Gromadzki
Ale co nie działa? Błąd? Nie ładuje się od razu? Kod wykonuje się niezależnie od wątku interfejsu i kiedy będzie miał dane od serwera wyświetli je w labelach nie zatrzymując interfejsu.
Daniel Urbaniec
  • Rejestracja:ponad 5 lat
  • Ostatnio:prawie 5 lat
  • Lokalizacja:Kraków
  • Postów:40
0
Yukiteru Gromadzki

Po prostu nie działa. Wyskakuje takie ostrzeżenie, lecz po załadowaniu aplikacji całość nie działa.

DY
  • Rejestracja:około 6 lat
  • Ostatnio:około miesiąc
  • Postów:75
2

Generalnie Task użyty tutaj służy do tego, żeby okno użytkownika się załadowało i mógł on na nim wykonywać akcje, a same dane zostały doczytane "na boku" i zaktualizowały się, kiedy zostaną pobrane. Więc nie wiem, czy dobrze zrobiłeś wrzucając "HomePage hmp = new HomePage();" w LoadServerInfo();

Poza tym kod stworzony przez ldilley ma pewną wadę, a mianowicie ustawia timeout dla TcpClient tylko podczas odbioru wiadomości.
Proponuję w nim zmienić kawałek:

Kopiuj
                tcpclient.ReceiveTimeout = Timeout;
                stopWatch.Start();
                tcpclient.Connect(address, port);

Na:

Kopiuj
                tcpclient.ReceiveTimeout = Timeout;
                tcpclient.SendTimeout = Timeout;
                stopWatch.Start();
                var result = tcpclient.BeginConnect(address, port, null, null);
                var success = result.AsyncWaitHandle.WaitOne(Timeout);
                if (!success)
                {
                    throw new Exception("Connection timed out.");
                }
edytowany 6x, ostatnio: Dyzma
Daniel Urbaniec
  • Rejestracja:ponad 5 lat
  • Ostatnio:prawie 5 lat
  • Lokalizacja:Kraków
  • Postów:40
0
Dyzma napisał(a):

Generalnie Task użyty tutaj służy do tego, żeby okno użytkownika się załadowało i mógł on na nim wykonywać akcje, a same dane zostały doczytane "na boku" i zaktualizowały się, kiedy zostaną pobrane. Więc nie wiem, czy dobrze zrobiłeś wrzucając "HomePage hmp = new HomePage();" w LoadServerInfo();

Poza tym kod stworzony przez ldilley ma pewną wadę, a mianowicie ustawia timeout dla TcpClient tylko podczas odbioru wiadomości.
Proponuję w nim zmienić kawałek:

Kopiuj
                tcpclient.ReceiveTimeout = Timeout;
                stopWatch.Start();
                tcpclient.Connect(address, port);

Na:

Kopiuj
                tcpclient.ReceiveTimeout = Timeout;
                tcpclient.SendTimeout = Timeout;
                stopWatch.Start();
                var result = tcpclient.BeginConnect(address, port, null, null);
                var success = result.AsyncWaitHandle.WaitOne(Timeout);
                if (!success)
                {
                    throw new Exception("Connection timed out.");
                }

Zrobiłem tak jak powiedziałeś, lecz nic to nie dało :/
Nadal cała aplikacja zacina się podczas ładowania informacji o serwerze + pokazuje, że serwer jest offline podczas gdy jest włączony.

DY
  • Rejestracja:około 6 lat
  • Ostatnio:około miesiąc
  • Postów:75
0

A zrobiłeś coś z "HomePage hmp = new HomePage();" w LoadServerInfo(); ?

Daniel Urbaniec
  • Rejestracja:ponad 5 lat
  • Ostatnio:prawie 5 lat
  • Lokalizacja:Kraków
  • Postów:40
0
Dyzma napisał(a):

A zrobiłeś coś z "HomePage hmp = new HomePage();" w LoadServerInfo(); ?

Edytowałem ten element, który napisałeś wyżej, a w Window_Loaded zmieniłem cały kod na ten, który był na samym początku.

Kopiuj
        private void Window_Loaded(object sender, EventArgs e)
        {
            MineStat ms = new MineStat("mc.skkf.net", 25565);
            if (ms.ServerUp)
            {
                serverstatus2.Text = ms.CurrentPlayers;
            }
            else
                serverstatus2.Text = "Offline";
DY
  • Rejestracja:około 6 lat
  • Ostatnio:około miesiąc
  • Postów:75
1

Poprawka, którą zaproponowałem do kodu ldilley ma na celu jedynie umożliwienie ustawienia czasu połączenia i wysyłania wiadomości, po którym nastąpi timeout.
Sama w sobie nie rozwiąże problemu, bo jak pisałem, po to jest Task. Żeby umożliwić interakcje dla użytkownika, kiedy trwa połączenie z serwerem i pobieranie danych.
Dalej potrzebujesz Task'ów. Wrzucasz jedynie kawałki kodu i do tego partiale, więc nie wiem, jak Ci to pokazać konkretnie, bo nie znam flow w Twoim programie.

Daniel Urbaniec
  • Rejestracja:ponad 5 lat
  • Ostatnio:prawie 5 lat
  • Lokalizacja:Kraków
  • Postów:40
0
Dyzma napisał(a):

Poprawka, którą zaproponowałem do kodu ldilley ma na celu jedynie umożliwienie ustawienia czasu połączenia i wysyłania wiadomości, po którym nastąpi timeout.
Sama w sobie nie rozwiąże problemu, bo jak pisałem, po to jest Task. Żeby umożliwić interakcje dla użytkownika, kiedy trwa połączenie z serwerem i pobieranie danych.
Dalej potrzebujesz Task'ów. Wrzucasz jedynie kawałki kodu i do tego partiale, więc nie wiem, jak Ci to pokazać konkretnie, bo nie znam flow w Twoim programie.

Działanie aplikacji odbywa się następująco:

  • progressbar z pobieraniem javy
  • okno ze stroną logowania
  • następne okno ze stroną główną
  • reszta stron odpowiadających za poszczególne funkcje

Problem leży w tym, że po zalogowaniu się przez użytkownika przerzuca go z okna logowania do okna ze stroną główną na której znajduje się informacja o tym czy serwer jest aktualnie aktywny czy też nie. W momencie przerzucenia ładowane są dane na temat aktywności serwera przez co dochodzi do około 10/15s zwieszenia się programu przed ukazaniem informacji.
Moim pytaniem było czy jest opcja zmniejszenia do minimum / wyeliminowania tego zwieszenia.

DY
  • Rejestracja:około 6 lat
  • Ostatnio:około miesiąc
  • Postów:75
0

Jedyne co możesz zrobić, jeśli odpowiedź trwa tak długo (swoją drogą, to ciekawy serwer skoro tyle odpowiada), to użyć w/w task'ów.
Wtedy w momencie kiedy użytkownikowi pokazuje się strona główna nie czytasz od razu danych z serwera.
Ładujesz wszystko co potrzebne poza tymi danymi, a pobranie tych danych odpalasz "w tle" właśnie na oddzielnym task'u.
Efekt będzie taki, że użytkownik wbije na stronę główną, danych nie zobaczy, bo będą puste (można dodać jakiegoś progres bara "pobieranie danych w toku"),
ale będzie mógł od razu korzystać z tej strony.

edytowany 1x, ostatnio: Dyzma
Daniel Urbaniec
  • Rejestracja:ponad 5 lat
  • Ostatnio:prawie 5 lat
  • Lokalizacja:Kraków
  • Postów:40
0
Dyzma napisał(a):

Jedyne co możesz zrobić, jeśli odpowiedź trwa tak długo, to użyć w/w task'ów.
Wtedy w momencie kiedy użytkownikowi pokazuje się strona główna nie czytasz od razu danych z serwera.
Ładujesz wszystko co potrzebne poza tymi danymi, a pobranie tych danych odpalasz "w tle" właśnie na oddzielnym task'u.
Efekt będzie taki, że użytkownik wbije na stronę główną, danych nie zobaczy, bo będą puste (można dodać jakiegoś progres bara "pobieranie danych w toku"),
ale będzie mógł od razu korzystać z tej strony.

Szczerze mówiąc jestem początkujący w sprawie pisania aplikacji, staram się być samoukiem i czerpać z Internetu i porad innych jak najwięcej.
Rozumiem o co Ci chodzi i w jaki sposób miało by to funkcjonować, zwizualizowałem to sobie w głowie, lecz nie mam pomysłu jak stworzyć takowy proces w tle.

DY
  • Rejestracja:około 6 lat
  • Ostatnio:około miesiąc
  • Postów:75
1

Ten proces w tle to właśnie ten fragment kodu:

Kopiuj
        public static async Task LoadServerInfo()
        {
            await Task.Run(() =>
            {
                MineStat ms = new MineStat("mc.skkf.net", 25565);
                serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Server is offline!";
            });
        }

Dorzuciłeś wcześniej do niego:

Kopiuj
HomePage hmp = new HomePage();

Co może nie być najlepszym pomysłem, bo ten obiekt wydaje mi się powinien już istnieć w momencie wywołania LoadServerInfo().
Jedyne co ma robić ta funkcja to uzupełnić danymi istniejący obiekt.
Czyli ładujesz okno główne dla użytkownika, kończy się ładowanie kontrolek i wszystkich innych funkcjonalności, wtedy wołasz funkcję LoadServerInfo(),
która zaczyna w tle pobierać dane i jak skończy uzupełni puste pola w oknie głównym.

Radzę poczytać o taskach w C#. Wbrew pozorom podstawowa wielowątkowość oparta właśnie o taski jest względnie prosta i rozwiązuje wiele problemów. ;)
(Czasami inne generuje, ale to już inna bajka...)

edytowany 1x, ostatnio: Dyzma
Daniel Urbaniec
  • Rejestracja:ponad 5 lat
  • Ostatnio:prawie 5 lat
  • Lokalizacja:Kraków
  • Postów:40
0
Dyzma napisał(a):

Dorzuciłeś wcześniej do niego:

Kopiuj
HomePage hmp = new HomePage();

Co może nie być najlepszym pomysłem, bo ten obiekt wydaje mi się powinien już istnieć w momencie wywołania LoadServerInfo().
Jedyne co ma robić ta funkcja to uzupełnić danymi istniejący obiekt.
Czyli ładujesz okno główne dla użytkownika, kończy się ładowanie kontrolek i wszystkich innych funkcjonalności, wtedy wołasz funkcję LoadServerInfo(),
która zaczyna w tle pobierać dane i jak skończy uzupełni puste pola w oknie głównym.

To jest aktualny początek strony

Kopiuj
    public partial class HomePage : Page
    {
        public HomePage()
        {
            InitializeComponent();

        }

Gdy nie stworzę HomePage hmp = new HomePage(); i nie wstawię go w to miejsce hmp.serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Server is offline!"; to całość wyrzuca błąd, iż serverstatus2.Text musi mieć odwołanie do obiektu, chociaż obiekt jest na tej stronie stworzony.
Dopiero po wrzuceniu hmp. całość usuwa błąd.

DY
  • Rejestracja:około 6 lat
  • Ostatnio:około miesiąc
  • Postów:75
0

Pokaż kod jak możesz. Żeby było widać gdzie wołasz metody, gdzie tworzysz obiekt i czy jedno do drugiego ma dostęp.
Bo jakby co, ten obiekt stwórz wcześniej. Tak, żeby metoda mogła się do niego odwołać.

edytowany 1x, ostatnio: Dyzma
Daniel Urbaniec
  • Rejestracja:ponad 5 lat
  • Ostatnio:prawie 5 lat
  • Lokalizacja:Kraków
  • Postów:40
0
Dyzma napisał(a):

Pokaż kod jak możesz. Żeby było widać gdzie wołasz metody, gdzie tworzysz obiekt i czy jedno do drugiego ma dostęp.
Bo jakby co, ten obiekt stwórz wcześniej. Tak, żeby metoda mogła się do niego odwołać.

Kopiuj
namespace launchek
{
    /// <summary>
    /// Logika interakcji dla klasy HomePage.xaml
    /// </summary>
    public partial class HomePage : Page
    {
        public static async Task LoadServerInfo()
        {
            HomePage hmp = new HomePage();
            await Task.Run(() =>
            {
                MineStat ms = new MineStat("mc.skkf.net", 25565);
                hmp.serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Offline";
            });
        }
        public HomePage()
        {
            InitializeComponent();
        }


        MProfileInfo[] versions;
        MSession session;
        GamePatch Patcher;

        private void Window_Loaded(object sender, EventArgs e)
        {
            LoadServerInfo();

            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();
        }
    }
}

Co do LoadServerInfo(); w Window_Loaded wyskakuje mi ostrzeżenie: "CS4014 Ponieważ to wywołanie nie jest oczekiwane, wykonywanie bieżącej metody będzie kontynuowane bez oczekiwania na ukończenie wywołania. Rozważ możliwość zastosowania operatora „await” do wyniku wywołania."

DY
  • Rejestracja:około 6 lat
  • Ostatnio:około miesiąc
  • Postów:75
1

Co do ostrzeżenia, wynika ono z tego, że nigdzie nie czekasz, aż to się wywoła. Ale Ty nie chcesz czekać, więc jest ok. ;)
Spróbuj tak:

Kopiuj
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)
        {
            HomePage hmp = new HomePage();
            Task.Run(() =>
            {
                MineStat ms = new MineStat("mc.skkf.net", 25565);
                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();
        }
    }
}
edytowany 1x, ostatnio: Dyzma
Daniel Urbaniec
  • Rejestracja:ponad 5 lat
  • Ostatnio:prawie 5 lat
  • Lokalizacja:Kraków
  • Postów:40
0
Dyzma napisał(a):

Co do ostrzeżenia, wynika ono z tego, że nigdzie nie czekasz, aż to się wywoła. Ale Ty nie chcesz czekać, więc jest ok. ;)

Całość załączyła się normalnie, lecz po dojściu do momentu hmp.serverstatus2 = ms.ServerUp ? ms.CurrentPlayers : "Offline"; wyskakuje błąd "System.InvalidOperationException: „Wątek wywołujący nie może uzyskać dostępu do tego obiektu, ponieważ należy on do innego wątku.”
Stworzyłem string:

Kopiuj
        private void Window_Loaded(object sender, EventArgs e)
        {
            string status2 = serverstatus2.Text;
            Task.Run(() =>
            {
                MineStat ms = new MineStat("mc.skkf.net", 25565);
                status2 = ms.ServerUp ? ms.CurrentPlayers : "Offline";
            });

Po uruchomieniu całość nie wyrzuca żadnego błędu, lecz z napisem status nic się nie dzieje.

Dane wyjściowe:
"Wątek 0xf08 zakończył działanie z kodem 0 (0x0).
Zgłoszony wyjątek: „System.Exception” w launchek.exe
Wątek 0x4178 zakończył działanie z kodem 0 (0x0).
Wątek 0x3c90 zakończył działanie z kodem 0 (0x0).
Wątek 0x5254 zakończył działanie z kodem 0 (0x0).
Wątek 0x1ac0 zakończył działanie z kodem 0 (0x0).
Wątek 0x25d0 zakończył działanie z kodem 0 (0x0)."

DY
  • Rejestracja:około 6 lat
  • Ostatnio:około miesiąc
  • Postów:75
2

Ach sorry, zapomniałem. Nie zrobisz tego bezpośrednio, bo GUI leci na innym wątku.
Zmień:

Kopiuj
            Task.Run(() =>
            {
                MineStat ms = new MineStat("mc.skkf.net", 25565);
                hmp.serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Offline";
            });

Na:

Kopiuj
            Task.Run(() =>
            {
                MineStat ms = new MineStat("mc.skkf.net", 25565);
                if (hmp.serverstatus2.Text.InvokeRequired)
                    hmp.serverstatus2.Text.Invoke(new Action(() => hmp.serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Offline"));
                else
                    hmp.serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Offline";
            });

EDIT: Poprawiłem nazwy kontrolki.

edytowany 3x, ostatnio: Dyzma
Daniel Urbaniec
  • Rejestracja:ponad 5 lat
  • Ostatnio:prawie 5 lat
  • Lokalizacja:Kraków
  • Postów:40
0
Dyzma napisał(a):

Ach sorry, zapomniałem. Nie zrobisz tego bezpośrednio, bo GUI leci na innym wątku.

Zmieniłem, lecz pojawia się błąd "CS1061 Element „string” nie zawiera definicji „Invoke” i nie odnaleziono dostępnej metody rozszerzenia „Invoke”, która przyjmuje pierwszy argument typu „string” (czy nie brakuje dyrektywy using lub odwołania do zestawu?)."

Pamiętam, że też miałem jakiś problem z Invoke w C# i dodawałem "System.Windows..." przed, ale nie pamiętam jak to dokładnie się robiło.

DY
  • Rejestracja:około 6 lat
  • Ostatnio:około miesiąc
  • Postów:75
1

Ponownie sorry, piszę w przerwach w pracy i za dużo copy-paste zrobiłem.
Poprawiona wersja:

Kopiuj
            Task.Run(() =>
            {
                MineStat ms = new MineStat("mc.skkf.net", 25565);
                if (hmp.serverstatus2.InvokeRequired)
                    hmp.serverstatus2.Invoke(new Action(() => hmp.serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Offline"));
                else
                    hmp.serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Offline";
            });
Daniel Urbaniec
  • Rejestracja:ponad 5 lat
  • Ostatnio:prawie 5 lat
  • Lokalizacja:Kraków
  • Postów:40
0
Dyzma napisał(a):

Ponownie sorry, piszę w przerwach w pracy i za dużo copy-paste zrobiłem.

Rozumiem, ale i tak się cieszę, że ktokolwiek zainteresował się tym, żeby mi pomóc i jestem wdzięczny.

Teraz pokazuje mi informację o tym, że TextBlock nie zawiera tej definicji.
Jeszcze dodając - zapomniałem wspomnieć, że piszę to w C# WPF. O ile się nie mylę jest to dosyć istotna informacja, a zapomniałem jej dodać :/
Aktualny kod:

Kopiuj
        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.InvokeRequired)
                    hmp.serverstatus2.Invoke(new Action(() => hmp.serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Offline"));
                else
                    hmp.serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Offline";
            });

Błąd: "CS1061 Element „TextBlock” nie zawiera definicji „Invoke” i nie odnaleziono dostępnej metody rozszerzenia „Invoke”, która przyjmuje pierwszy argument typu „TextBlock” (czy nie brakuje dyrektywy using lub odwołania do zestawu?)."

DY
  • Rejestracja:około 6 lat
  • Ostatnio:około miesiąc
  • Postów:75
1

Spoko, nie ma sprawy. ;)
Co do WPF'a, to zmienia postać rzeczy.
Zmień:

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

Na:

Kopiuj
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" } ));

Powinno zadziałać, ale za to już nie ręczę.

edytowany 1x, ostatnio: Dyzma
Ktos
Moderator
  • Rejestracja:prawie 23 lata
  • Ostatnio:około 3 godziny
2

Tę składnię Invoke można nieco uprościć:

Kopiuj
Dispatcher.Invoke(() => { hmp.serverstatus2.Text = ms.ServerUp ? ms.CurrentPlayers : "Offline" });
Daniel Urbaniec
  • Rejestracja:ponad 5 lat
  • Ostatnio:prawie 5 lat
  • Lokalizacja:Kraków
  • Postów:40
0
Ktos napisał(a):

Tę składnię Invoke można nieco uprościć:

Po uproszczeniu MineStat "Błąd IDE1008 'Element „MineStat” nie zawiera konstruktora, który przyjmuje wiele argumentów. "

Dyzma napisał(a):
Kopiuj
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" } ));

Powinno zadziałać, ale za to już nie ręczę.

Tutaj dodałem Dispatcher.Invoke

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

lecz przy InvokeRequired wyskakuje błąd: "Błąd CS1061 Element „TextBlock” nie zawiera definicji „InvokeRequired” i nie odnaleziono dostępnej metody rozszerzenia „InvokeRequired”, która przyjmuje pierwszy argument typu „TextBlock” (czy nie brakuje dyrektywy using lub odwołania do zestawu?)."

edytowany 1x, ostatnio: Daniel Urbaniec
DY
  • Rejestracja:około 6 lat
  • Ostatnio:około miesiąc
  • Postów:75
0

Zamiast:

Kopiuj
if (hmp.serverstatus2.InvokeRequired)

Ma być:

Kopiuj
if (hmp.serverstatus2.Dispatcher.CheckAccess())

Rzuć okiem na przykład ode mnie. ;)
Swoja drogą po "uproszczeniu" nie powinien rzucać takim błędów.
Zastosuj uproszczenie i wrzuć ponownie cały kod.

edytowany 1x, ostatnio: Dyzma
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)