IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 18 - Hra tetris v MonoGame: Webový klient

V minulé lekci, Hra tetris v MonoGame: Webový server, jsme si připravili jednoduchý PHP server s MySQL databází.

V dnešní bonusové lekci si ukážeme jak na tento server skóre odeslat a také jak internetové skóre v naší hře zobrazit. Tak směle do toho.

WebKlient

Po vzoru třídy SkoreKlient si nyní vytvoříme novou třídu WebKlient. Ta nebude se skóre pracovat lokálně, ale přes internet. Jinak bude fungovat úplně stejně. Rovněž bude třeba upravit komponentu KomponentaSkoreTabulka. A to je celé.

Třída WebKlient bude mít metody Uloz(), Nacti() a nově i Stahni(). Přidáme ještě konstanty pro držení URL potřebných pro uložení a načtení.

Třída bude vypadat takto:

public class WebKlient
{
    public string ChybaNacitani => $"Chyba při načítání skóre z internetu.";

    // přidáno
    private WebClient webKlient;
    private const string urlPridat = "http://localhost:8000/uloz";
    private const string urlVypsat = "http://localhost:8000/stahnout";


    private string soubor;

    public WebKlient()
    {
        webKlient = new WebClient();
        soubor = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"robotris\skore.xml");
            Directory.CreateDirectory(System.IO.Path.GetDirectoryName(soubor));
    }

    public void Uloz(Hrac hrac)
    {
        // Doplníme později
    }

    public void Stahni()
    {
        // Doplníme později
    }

    public List<Hrac> Nacti()
    {
        // Doplníme později
    }

}

Nezapomeňte si upravit 2 URL adresy na vaše skripty. Mohly by vypadat např. takto:

private const string urlPridat = "https://www.mujweb.cz/robotris_skore_uloz.php";
private const string urlVypsat = "https://www.mujweb.cz/robotris_skore_nacti.php";

Všimli jste si určitě nového atributu typu WebClient. Tato .NET třída obsahuje vše potřebné pro vykonání HTTP dotazu a zabaluje je do jednoduchých metod, které záhy použijeme. Jsou to OpenRead() a DownloadFile(). WebClient je třeba naimportovat pomocí přidání using System.Net; mezi usingy na začátku souboru.

Konstruktor

V třídě WebKlient neinicializujeme proměnnou soubor hned při její deklaraci, ale až v konstruktoru, protože chceme popsat cestu k souboru a vytvořit složku.

K vytvoření cesty je velmi užitečné používání metody Path.Combine(), která bere jednotlivé části cesty jako parametry a vytvoří z nich jednu validní cestu k souboru či složce. Nemusíme se tak zabývat chybějícími nebo přebývajícími lomítky a podobně.

Další užitečná metoda je Environment.GetFolderPath(), která nám vrátí cestu k nějaké systémové složce. Výraz Environment.SpecialFolder.ApplicationData získá ze speciálních složek složku dat aplikací. Microsoft má několik speciálních složek, které může naše aplikace využít pro ukládání dat potřebných pro svůj běh, např. uložené pozice ve hře, a každý uživatel počítače má pak své vlastní.

A jako poslední v aplikační složce vytvoříme novou složku robotris/ se souborem skore.xml.

Ukládání

Pro uložení skóre musíme vytvořit URL s QueryString (to je ta část za otazníkem), v němž bude přezdívka a dosažené body. Využijeme třídu Hrac a z ní vytáhneme vše potřebné. Pro formátování stringu použijeme notaci $"". Poté použijeme již zmíněnou metodu OpenUrl() třídy WebClient. Celá metoda Uloz() bude vypadat takto:

public void Uloz(Hrac hrac)
{
    string url = $"{urlPridat}?prezdivka={hrac.prezdivka}&body={hrac.body}&rady={hrac.rady}&level={hrac.level}";
    webKlient.OpenRead(url);
}

Stažení

Abychom ze serveru dostali třídu Hrac, lépe řečeno seznam hráčů List<Hrac>, je nutné nejdříve získat soubor XML. Využijeme tedy metodu DownloadFile() na WebClient. Ta požaduje URL, kde nalezne soubor a dále cestu, kam má soubor uložit. Metoda Stahni() bude vypadat takto:

public void Stahni()
{
    webKlient.DownloadFile(urlVypsat, soubor);
}

Načtení

Soubor máme stažený a teď ho ještě potřebujeme deserializovat na seznam hráčů a použít v naší tabulce.

Zbývá doplnit metodu Nacti(). Metoda bude identická se stejnojmennou metodou ve třídě SkoreKlient. Pro lepší udržitelnost a správu kódu bychom se ideálně měli vyhnout duplicitám v logice aplikace, ale doufám že mi to pro tentokrát prominete :)

public List<Hrac> Nacti()
{
    string element = "";
    Hrac hrac = new Hrac();
    List<Hrac> hraci = new List<Hrac>();
    // parsování XML
    using (XmlReader xr = XmlReader.Create(soubor))
    {
        // postupné načítání uzlů
        while (xr.Read())
        {
            // uzel typu element
            if (xr.NodeType == XmlNodeType.Element)
                element = xr.Name;  // uložení jména elementu
            else
                if (xr.NodeType == XmlNodeType.Text) // uzel typu text
                {
                    switch (element)
                    {
                        case "prezdivka":
                            hrac.prezdivka = xr.Value;
                            break;
                        case "body":
                            hrac.body = long.Parse(xr.Value);
                            break;
                    }
                }
            // konec elementu hráč
                else if ((xr.NodeType == XmlNodeType.EndElement) && (xr.Name == "hrac"))
                {
                    hraci.Add(hrac);
                    hrac = new Hrac();
                }
        }
    }
    return hraci;
}

To je vše k našemu webovému klientovi. Teď zbývá jen provést změny k komponentě pro vykreslení skóre a máme hotovo

KomponentaSko­reTabulka

Zde přidáme nový atribut jménem webKlient, který bude držet instanci naší nové třídy:

private WebKlient webKlient;

Atribut nezapomeneme inicializovat v metodě Initialize():

webKlient = new WebKlient();

Ještě přidáme nový stav do výčtu eStav a tím bude ChybaWebu. Tuto chybu nebudeme považovat za fatální a tedy pokud se vyskytne, dáme uživateli vědět a zeptáme se, zda bude chtít pokus o stažení opakovat nebo ne. Pokud zvolí ne, skóre zobrazíme pouze formou lokálně uložených dat. Enum eStav nyní vypadá takto:

private enum eStav
{
    ChybaNacteni,
    ChybaUlozeni,
    ChybaWebu,
    Zapis,
    Vypis,
    Otazka,
}

Nacti()

Změníme metodu Nacti(), ve které přidáme stažení a načtení skóre ze serveru a přidáme tyto hráče mezi hráče již lokálně načtené:

public void Nacti()
{
    stav = eStav.Vypis;
    hraci = new List<Hrac>();

    try
    {
        var xmlHraci = klient.Nacti();

        hraci.AddRange(xmlHraci);

    }
    catch
    {
        stav = eStav.ChybaNacteni;
        return;
    }

    try
    {
        webKlient.Stahni();
        var webHraci = webKlient.Nacti();

        hraci.AddRange(webHraci);
    }
    catch
    {
        stav = eStav.ChybaWebu;
    }

    hraci = hraci.OrderByDescending(x => x.body).ToList();
}

Nejprve načteme lokálně uložené hráče a pokud se vyskytne chyba, ukončíme vykonávanou metodu. Poté stáhneme data ze serveru, kde opět chytáme chybu a nakonec hráče seřadíme podle dosažených bodů.

K využítí metody řazení na seznamu OrderByDescending() je nutný using using System.Linq;.

Uloz()

Ukládání provedeme pokusem o uložení na server a pokud bude pokus neúspěšný, tak data uložíme lokálně. Nechceme přeci, aby uživatel přišel o těžce vydřené body :)

public void Uloz()
{
    try
    {
        webKlient.Uloz(hra.hrac);
        Nacti();
        stav = eStav.Vypis;
    }
    catch
    {
        try
        {
            klient.Uloz(hra.hrac);
            hraci = klient.Nacti();
            stav = eStav.Vypis;
        }
        catch
        {
            stav = eStav.ChybaUlozeni;
        }
    }
}

OnEnabledChanged()

Následuje upravení metody OnEnabledChanged():

protected override void OnEnabledChanged(object sender, EventArgs args)
{
    if (Enabled)
    {
        Nacti();
        // Pokud budeme ve stavu chyba přeskočíme
        if (!(stav == eStav.ChybaNacteni || stav == eStav.ChybaUlozeni))
        {
            // Rovnou na vypis pokud nejsou body
            if (hra.hrac != null && hra.hrac.body > 0)
                stav = eStav.Otazka;
            else
                if (stav != eStav.ChybaWebu)
                    stav = eStav.Vypis;
        }
    }
    base.OnEnabledChanged(sender, args);
}

Zde načteme skóre a při chybě webu nejdříve zjistíme, zda má hráč něco k uložení. Pokud nemá, tak teprve nastavíme stav na výpis, pokud není chyba webu. Tímto umožníme hráči uložit skóre, pokud nastala chyba v komunikaci se serverem.

Draw() a Update()

A dostali jsme se k závěru, kde upravíme metody Draw() a Update(), ve kterých budeme reagovat na námi přidaný nový stav ChybaWebu:

V metodě Draw() přidáme do switch:

// vykreslení chyby webu
case eStav.ChybaWebu:
    hra.spriteBatch.TextSeStinem(hra.fontCourierNew, $"{webKlient.ChybaNacitani} \n Zkusit znovu? [A/N]", new Vector2(510, 240), Color.Red);
    break;

A do metody Update() rovněž do switch přidáme další případ:

// dotaz při chybě skore na web
case eStav.ChybaWebu:
    if (hra.NovaKlavesa(Keys.A))
        Nacti();
    if (hra.NovaKlavesa(Keys.N))
        stav = eStav.Vypis;
        break;

Pokud se vyskytla chyba webu, tak ji zobrazíme. A pokud uživatel nebude chtít akci opakovat, vypíšeme skóre.

PHP server

PHP server jsme si vytvořili minule a aplikaci s ním můžete nyní vyzkoušet.

C# server

Pokud jste měli problémy s PHP serverem nebo jej chcete v C#, tak slíbený C# server je součástí kódů k této lekci. Po rozbalení je ve složce RobotrisServer/. Aplikace má název robotris_web_server/ a vytvoří server, který poslouchá na adrese a portu http://localhost:8000. Obsahuje pouze dvě URL, které jsou uloz a stahnout. Aplikace vytvoří ve své složce XML soubor s uloženým skóre, který funguje jako "webové" uložiště. Tento XML je neplatně formátován.

  • Po dotazu uloz server vytáhne z QueryStringu parametry prezdivka, body, rady a level. Parametr prezdivka je text. Zbylé hodnoty musí být číslo. Uloží se do předem zmíněného souboru.
  • Po dotazu na stahnout se obsah předem zmíněného souboru zformátuje na platný XML a odešle. A tento soubor už v Robotrisu získáváme, ukládáme a deserializujeme.

Serverové hlášky jsou v angličtině. A vypisují se do konzole aplikace.

Toto je konec bonusové lekce o tom jak ukládat skóre na web. Pokud jste došli až jsem tak děkuji za pozornost a v nějákem dalším tutoriálu nebo článku nashledanou :)


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 22x (13.93 MB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

Předchozí článek
Hra tetris v MonoGame: Webový server
Všechny články v sekci
Od nuly k tetrisu v MonoGame
Článek pro vás napsal Matouš Kratochvíl
Avatar
Uživatelské hodnocení:
2 hlasů
Autor se věnuje C#
Aktivity