📜 ⬆️ ⬇️

Fool app for Windows Store


Paul Cezanne, "Card Players"

A long time ago, in Windows 95, there was a Microsoft Hearts game. The game of cards on the network, with opponents around the world. If my memory serves me, in Windows for Workgroups 3.11 (yes, I caught all these artifacts!) There was a version for playing on a local network, using the so-called NetDDE.

Card game did not have to choose a long time. As they say, the rich ... High bridge and preference have disappeared because of their complete ignorance. There was only one thing left - to disappear the fool.

The situation was complicated by the fact that so far I have never been involved in developing a “backend”. Googling led me right to the right place - SignalR .
')
Here I would like to say a few enthusiastic words to SignalR. A well-documented library that best fit my needs. Bores will say that it is only under Windows, well, let them grit their teeth with envy. Although there seems to be her kolkhoz clients for iOS, I have not studied this question in detail.

Next question hosting. Then I did not think long, Azure was at hand.

So, what did I want?



What do I use from Azure?



Everything.

Recently, I also used their push notification service. But it seemed expensive (10 bucks a month), moreover, it turned out that because of the Microsoft billing glitch I had been paying for more than a year for these two services! Goring with support led to the fact that they admitted a mistake and offered a month of compensation. After some time, I completely abandoned this service, adding another table to my database for storing signatories on the push and sending them myself from the main application.

At a given time, the cost of hosting monthly is about 400 p. This is only the cost of the SQL server. I have a small outgoing traffic and it fits into the free 5 GB per month.

Development


The development took place at Visual Studio 2015, MVVM framework MVVM light was used for the client.

A little server "kitchen" (aesthetes and the faint of heart is better not to look)


connecting users, sending out pushes
public override Task OnConnected() { if (((DateTime.Now - LastPush).TotalSeconds) > 360) { LastPush = DateTime.Now; Task.Run(() => SendNotifications()); } return base.OnConnected(); } 


create anonymous game
 /// <summary> ///   .        /// </summary> /// <returns>ID  </returns> async public Task<String> 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(); } } 


game room creation
 /// <summary> ///    /// </summary> /// <returns>   </returns> 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(); } } 


peep the top card in the deck
 /// <summary> ///     - /// </summary> /// <param name="gameid"></param> /// <returns></returns> 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(); } } } 


send a message to your opponent
 /// <summary> ///  /// </summary> /// <param name="gameid">ID  (  )</param> /// <param name="ChatMessage"> </param> /// <returns></returns> 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(); } } } 



About client


To identify the players, the LiveId functionality was used initially, then the Graph API. When you first start the application, the player is asked to provide access to his account (from it I take only the name and the so-called anonymous id, which looks something like this: "ed4dd29dda5f982a"). However, the player can play anonymously, but then the statistics of his games is not conducted.

For each non-anonymous player are stored:

1. date of the first game / date of the last game
2. name / nickname of the player
3. the number of games played / how many of them are won
4. last IP address
5. received prizes

If there are two non-anonymous players in the game, then before it starts, I get the statistics of the games of these particular players (how many games they played with each other and who won how many). For this purpose, the resulting anonymous id is used in the SQL query.

In the screenshot at the top left you can see an example (clickable):



Screenshot of general statistics (clickable):



In addition, there is a “competition” across countries (anonymous players participate here, information is taken from the IP address):



Players can exchange short messages.

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

An example of a situation handler is “I take the cards, and the opponent adds me after the game”:

 FoolHubProxy.On<byte, bool>("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)); 

About prizes, cookies, etc.


Every month, the top five players who have scored the most victories, receive a badge of "For the capture of Berlin . " Top 50 participants are awarded badges for the best winning percentage (also five). In addition, there are badges for winning “express” (a situation where you have 2, 3 or 4, say, sixes or jacks on the last move). Then they are laid out on the table all at once and you are a fine fellow. There are cookies for victory, when an opponent gives you "in the list". He also gets a consolation, with a skull and bones.

About any additional functionality


The application is free, but it has various additional "buns", designed as InApp Purchases:


Conclusion


As a result of the development of this application, I:


Question


And how on Habré is the case with the scattering of money from a helicopter distribution in PM promotional codes to those who want it? If you do not give a kick for it, please contact us.

Source: https://habr.com/ru/post/442746/


All Articles