Fool App für den Windows Store


    Paul Cezanne, "Kartenspieler"

    Es war einmal ein Microsoft Hearts-Spiel unter Windows 95. Online Karten spielen mit Gegnern auf der ganzen Welt. Wenn der Speicher mir recht macht, dann gab es in Windows für Workgroups 3.11 (ja, ich habe all diese Artefakte gefunden!) Eine Version für das Spielen in einem lokalen Netzwerk mit dem sogenannten NetDDE.

    Ich musste lange kein Kartenspiel auswählen. Wie das Sprichwort sagt, was ist reich ... Die Hochregalbrücke und die Vorliebe fielen aufgrund ihrer völligen Unwissenheit weg. Es blieb nur eines übrig - als dummer Dummkopf zu verschwinden .

    Die Situation wurde durch die Tatsache erschwert, dass ich bisher noch nie an der Entwicklung eines „Backends“ beteiligt war. Googeln führte mich an den richtigen Ort - SignalR .

    Hier möchte ich SignalR ein paar begeisterte Worte sagen. Eine gut dokumentierte Bibliothek, die perfekt zu meinen Bedürfnissen passt. Bohrungen werden sagen, dass es nur für Windows ist, nun, lassen Sie sie ihre Zähne vor Neid zusammenbeißen. Obwohl es scheinbar Kollektiv-Farm-Clients für iOS gibt, habe ich dieses Problem nicht im Detail untersucht.

    Die nächste Frage ist Hosting. Dann habe ich nicht lange nachgedacht, Azure war unter meinen Armen.

    Also, was wollte ich?


    • Die Art und Weise, wie Spieler eine Verbindung herstellen, sollte Microsoft Hearts ähneln. Die Spieler verbinden sich nacheinander, sobald der richtige Betrag erreicht ist - das Spiel beginnt. Für mich selbst habe ich beschlossen, mich auf ein Einzelspiel zu beschränken - und niemand zieht!
    • Am Anfang wird es nur wenige Spieler geben - wie erfahren sie voneinander? Dann kam die Idee auf, alle zu senden, die Push-Benachrichtigungen in dem Moment spielen möchten, in dem jemand die Anwendung startet und eine Verbindung zum Spieleserver herstellt. Um Benutzer nicht mit Stößen zu drosseln, machte er eine Einschränkung "nicht mehr als einmal alle N Minuten".
    • Ich möchte detaillierte Statistiken, Preise, Erfolge usw.
    • Ich möchte Karten mit verschiedenen Motiven
    • Ich möchte mit meinem Freund spielen und nicht mit wem "wen Gott gesandt hat".

    Was verwende ich mit Azure?


    • AppService ist in der Tat eine Anwendung, mit der sich alle Clients verbinden.
    • SQL Server + SQL Datenbank - zum Speichern von Spielstatistiken.

    Das ist alles.

    Kürzlich habe ich auch den Push-Benachrichtigungs-Verteilungsdienst verwendet. Aber es schien teuer zu sein (10 Dollar pro Monat), und es stellte sich heraus, dass ich aufgrund einer Microsoft-Abrechnungsstörung für diese beiden Dienste mehr als ein Jahr lang bezahlt hatte! Die Unterstützung durch die Partner führte dazu, dass sie den Fehler erkannten und bis zu einem Monat Entschädigung boten. Nach einiger Zeit habe ich diesen Dienst vollständig aufgegeben und meiner Datenbank eine weitere Tabelle hinzugefügt, um Signierer für Push zu speichern und sie selbst aus der Hauptanwendung zu senden.

    Zu diesem Zeitpunkt betragen die Kosten für das Hosting pro Monat ca. 400 p. Dies sind nur die Kosten für den SQL Server. Ich habe kleinen ausgehenden Verkehr und es passt in die freien 5 GB pro Monat.

    Entwicklung


    Die Entwicklung erfolgte in Visual Studio 2015, für den Client wurde das MVVM-Framework MVVM light verwendet.

    Ein kleines Server "Küche" (Ästheten und schwache Nerven ist besser, nicht zu sehen)


    Benutzerverbindung, drücken
    public override Task OnConnected()
            {
                if (((DateTime.Now - LastPush).TotalSeconds) > 360)
                {
                    LastPush = DateTime.Now;
                    Task.Run(() => SendNotifications());
                }
                return base.OnConnected();
            }


    ein anonymes Spiel erstellen
    /// 
    /// старт анонимной игры. При наличии уже подключенного соперника начинается игра
    /// 
    /// ID начатой игры
    async public Task ConnectAnonymous(PlayerInformation pi)
            {
                MainSemaphore.WaitOne();
                try
                {
                    string res = String.Empty;
                    string p_ip = Context.Request.GetHttpContext().Request.UserHostAddress;
                    if (NextAnonymGame == null)
                    {
                        NextAnonymGame = new FoolGame(Context.ConnectionId, pi, p_ip, false);
                        res = NextAnonymGame.strGameID;
                    }
                    else
                    {
                        await NextAnonymGame.Start(Context.ConnectionId, pi, p_ip);
                        ActiveGames.Add(NextAnonymGame.strGameID, NextAnonymGame);
                        res = NextAnonymGame.strGameID;
                        NextAnonymGame = null;
                    }
                    return res;
                }
                finally
                {
                    MainSemaphore.Release();
                }
            }


    Schaffung eines Spielraums
    /// 
    /// создание игровой комнаты
    /// 
    /// кодовое слово игровой комнаты
    public String CreatePrivateGame(PlayerInformation pi)
            {
                MainSemaphore.WaitOne();
                try
                {
                    string p_ip = Context.Request.GetHttpContext().Request.UserHostAddress;
                    FoolGame game = new FoolGame(Context.ConnectionId, pi, p_ip, true);
                    WaitingPrivateGames.Add(game.strGameID, game);
                    return game.PrivatePass;
                }
                finally
                {
                    MainSemaphore.Release();
                }
            }


    Lies die oberste Karte im Stapel
    
    /// 
    /// возвращает верхнюю карту из стека-колоды
    /// 
    /// 
    /// 
    async public Task PeekCard(string gameid)
            {
                FoolGame game = null;
                game = ActiveGames.FirstOrDefault(games => games.Value.strGameID == gameid).Value;
                if (game != null)
                {
                    game.GameSemaphore.Wait();
                    await Task.Delay(35);
                    try
                    {
                        await Clients.Caller.PeekedCard(game.Deck.Peek());
                    }
                    finally
                    {
                        game.GameSemaphore.Release();
                    }
                }
            }


    Sende eine Nachricht an einen Gegner
    
    /// 
    /// чат
    /// 
    /// ID игры (и имя группы)
    /// текст сообщения
    /// 
    async public Task ChatMessage(string gameid, string ChatMessage)
            {
                FoolGame game = null;
                game = ActiveGames.FirstOrDefault(games => games.Value.strGameID == gameid).Value;
                if (game != null)
                {
                    game.GameSemaphore.Wait();
                    await Task.Delay(35);
                    try
                    {
                        await Clients.OthersInGroup(gameid).ChatMessage(ChatMessage);
                    }
                    finally
                    {
                        game.GameSemaphore.Release();
                    }
                }
            }
    



    Über den Kunden


    Zur Identifizierung der Player wurde zunächst die LiveId-Funktionalität und dann die Graph-API verwendet. Beim ersten Start der Anwendung wird der Spieler aufgefordert, Zugang zu seinem Konto zu gewähren (davon nehme ich nur den Namen und die sogenannte anonyme ID, die ungefähr so ​​aussieht: "ed4dd29dda5f982a"). Ein Spieler kann jedoch anonym spielen, aber dann werden keine Statistiken über seine Spiele gespeichert.

    Für jeden nicht anonymen Spieler wird Folgendes gespeichert:

    1. Datum des ersten Spiels / Datum des letzten Spiels
    2. Name / Spitzname des Spielers
    3. Anzahl der gespielten Spiele / Anzahl der gewonnenen Spiele
    4. Letzte IP-Adresse
    5. Erhaltene Preise

    Wenn zwei nicht anonyme Spieler im Spiel spielen, erhalte ich vor dem Start die Statistiken der Spiele dieser Spieler (wie viele Spiele sie miteinander gespielt haben und wer wie viel gewonnen hat). Hierzu wird die empfangene anonyme ID in der SQL-Abfrage verwendet.

    Im Screenshot oben links sehen Sie ein Beispiel (anklickbar): Screenshot der allgemeinen Statistik (anklickbar): Zusätzlich gibt es einen "Wettbewerb" nach Ländern (hier nehmen auch anonyme Spieler teil, Informationen werden von der IP-Adresse bezogen): Spieler können Kurznachrichten austauschen.













    FoolHubProxy.On("ChatMessage", (chatmessage) => synchrocontext.Post(delegate
                {
                    PlayChatSound();
                    ShowMessageToast(chatmessage);
                }, null));

    Ein Beispiel für einen Situationshandler: „Ich nehme Karten, aber der Gegner fügt mir etwas mehr hinzu“:

    FoolHubProxy.On("TakeOneMoreCard", (addedcard, lastcard) => synchrocontext.Post(delegate
                {
                    CardModel card = new CardModel(addedcard, DeckIndex);
                    CardsOnTable_Low.Add(card);
                    OpponentsCards.Remove(OpponentsCards.Last());
                    if (lastcard)
                    {
                        AppMode = AppModeEnum.defeated;
                    }
                }, null));

    Über Preise, Kekse usw.


    Jeden Monat erhalten die ersten fünf Spieler, die die meisten Siege errungen haben, das Abzeichen „Für die Eroberung Berlins“ . Teilnehmer in den Top 50 werden für den besten Prozentsatz der Siege (ebenfalls fünf) mit Abzeichen ausgezeichnet. Außerdem gibt es Abzeichen für den Gewinn mit "Express" (eine Situation, in der Sie im letzten Zug 2, 3 oder 4 haben, sagen wir sechs oder Buben). Dann haben sie alles auf einen Schlag auf den Tisch gelegt und Sie - gut gemacht. Es gibt Cookies für den Sieg, wenn der Gegner Ihnen eine "Liste" gibt. Er tröstet sich auch mit einem Schädel und Knochen.

    Über zusätzliche Funktionen


    Die Anwendung ist kostenlos, hat aber verschiedene zusätzliche "Brötchen", die als InApp-Käufe dekoriert sind:

    • Gruppieren Sie Ihre Karten nach Farbe und Tipps während des Spiels (wenn Sie schlagen oder werfen müssen, werden geeignete Karten nach oben gedrückt).
    • die Fähigkeit, dem Gegner nicht zu zeigen, wie viele Karten Sie in Ihren Händen haben. In einer normalen Situation sieht er es
    • die möglichkeit, immer als erster das spiel zu starten. Ansonsten wird der erste Zug zufällig gespielt.
    • die Fähigkeit, die abgeschlagenen Karten im Spiel zu sehen
    • die Möglichkeit, einmal pro Spiel in den Stapel zu schauen und die nächste Karte herauszufinden
    • die Fähigkeit zu betrügen. Sie können 3 Mal pro Spiel abwehren oder die falsche Karte werfen, in der Regel jede. Aber der Gegner hat die Möglichkeit, Ihnen die falsche Karte zurückzugeben, nachdem er dies bemerkt hat.

    Fazit


    Als Ergebnis der Entwicklung dieser Anwendung habe ich:

    • traf SignalR
    • aktualisiertes SQL im Speicher (Abfragen, gespeicherte Prozeduren, Funktionen)
    • Informationen zum Hosten von Apps in Azure
    • Vom Spielen des "Dummkopfs" verblüfft.

    Frage


    Und was ist mit der Situation in Habré, wo Geld aus einem Hubschrauber gestreut wird, indem Promo-Codes an interessierte Personen in einem persönlichen Bereich verteilt werden? Wenn sie dafür keinen Kick geben, wenden Sie sich an.

    Update


    YouTube: Aufnahme einiger Spiele

    Jetzt auch beliebt: