⬆️ ⬇️

Tanchiki in the console, the first article: "From the dispute to writing code"

Perhaps you need to start with a little background: I’m somehow sitting on a pair and we decided to argue with a group mate about the possibility of creating the simplest tanchik in the console (like dendivs), but for playing on the network.



Since we had no computer networks yet, I had to learn everything from scratch myself. Having read, perhaps, pages 30 of the selected text and having listened to four lectures on this topic, I felt very bored and lazy to listen to this further, and I finally started the project.



Are you all ready? Getting started!



This article will be short but informative (for beginners like me).

')

At the time of this writing, I knew only a few languages ​​and I was thinking about the choice of each of them and how it is suitable for the development of these same tanchiki. But based on knowledge, I decided to distribute everything as follows:



C # - client (as the easiest to learn language)

Rust - server (as the safest and fastest)

Php / html / css / javascript - site (which we MAYBE do)



Part One: Problem Statement



The main thing that I had to do in order to prove the correctness is simple tanchiki, but I decided to make a universal client. Like this? - This is when the server is equally optimized as for WinForm, and for the console (because I want good tunics in WinForm).



So our task sounds like this: It is necessary to develop three applications, the first for WinForm (standard windows), the second - the console (dandy emulator) and the third - the server itself.



Part Two: Ideas and Flaws ...



What should a good application do? - I asked this question when designing and the answer in my head was: “Be quick”.



What does it mean? - The fact that we have to work with several streams of receiving / transmitting data. The projectile cannot wait until the tank is rendered, the tank cannot wait while the wall is drawn to move.



We all know how offensive it is when you have a FPS drop or do not have time to draw something and kill you. Such situations should not be!



Thinking, I decided to distribute them that way:



1st Stream (Maine) - must send a pressed key to the server.

The 2nd stream should receive the coordinates of the tanks.

3rd coordinates of the walls.

4th coordinates of the shells.



It was also desirable to create a chat for the communication of tankers during the game (suddenly we will be in different rooms), but this has not yet been implemented.



Now there is some specifics with the streams, the first should be a simple sender (that is, organize the minimum calculations and send them to the server), the rest should always receive and draw everything in our application. In the form of code, everything will be like this:



static async void Tank_coordinate() { //   await Task.Run( () => { }); } static async void Coordinate_wall() { //   await Task.Run( () => { }); } static async void Shot() { //   await Task.Run( () => { /*  :     ,     */ }); } static void To_key() { //   } 


After all this, I had several options for organizing data to send them to the server, and then lectures went into battle. The choice was great: either to organize everything in Inte / String variables and draw through them, or create a structure for the data, I did not consider the objects. did not want to mess with links. After an hour of walking in the forums, I stopped at the second, since organizing data as a structure is much easier , and writing documentation will be more convenient (advice: if there are groups of data that are similar in purpose, it will be better to combine them into a structure, because it is much easier read the code and look for variables). Our new task is: to create a structure in which there will be fields responsible for the key pressed, the player’s coordinate, the angle of the tank and (for the console) the position of the last character and preferably some kind of draw control. What is the last thing for us? - so that the coordinates with which we work are not used (we increase / decrease / send to the server)



What were the mistakes? - you ask me.



Initially, I started to climb into the jungle of the http model (I wanted to do it on http), but after a certain amount of time n, it became clear to me that it would be better to do it on tcp (and it will be easier to grow smaller problems with rastom).



We decided on the flows and on the idea, what's next?



And then, friends, will be the most interesting.



Part Three: Creating the structure and method Main (). The end of the first stage of development



Immediately I will throw the code so that impatient readers will immediately copy it:



 //    //       /// <summary> ///  ( ) /// </summary> public struct player_coor { public static void new_player_coor (int x_, int y_, string dir_, ConsoleKey key_, int last_x_, int last_y_) { x = x_; y = y_; dir = dir_; key = key_; last_x = last_x_; last_y = last_y_; } static int x = 2;//  static int y = 2;// static string dir;//  static ConsoleKey key;//  static int last_x;// static int last_y;// 'y'  'x' } static void Main(string[] args) { } 


But this is not the most convenient code, the best option was proposed: norver



and it looks like this:



 //      public class Position { //    public int X { get; set; } public int Y { get; set; } public Position(int x, int y) { X = x; Y = y; } } //   ,  "player_coor" public struct PlayerState { //         //  ,     //      /// <summary> ///   /// </summary> /// <param name="startPosition"> </param> /// <param name="dir_">   ()</param> /// <param name="key_"> </param> /// <param name="lastPosition">  </param> public PlayerState(Position startPosition, int dir_, ConsoleKey key_, Position lastPosition) { StartPosition = startPosition; LastPosition = lastPosition; dir = dir_; key = key_; } private Position StartPosition { get; set; } private Position LastPosition { get; set; } //  static int dir; //  static ConsoleKey key; } static void Main(string[] args) { //    Position,   2, 2 var startPosition = new Position(2, 2); //    Position,   5, 10 var currentPosition = new Position(5, 10); //    PlayerState var currentState = new PlayerState(startPosition, int, ConsoleKey.UpArrow, currentPosition); //          Position Console.WriteLine("X={0}, Y={1}", startPosition.X, startPosition.Y); } 




A small remark: during the writing of the continuation of this article, I found out that it would be much easier to store a numeric variable than a text one, which is why we will store an integer value in the dir variable (that is, the position).



This is the simplest code, but it does a great job: it spreads our future application.



A small description of the threads: any thread is created through the System.Threading space. We create it the same way as the class instance, but in the stream argument we specify the void function.

After creating the stream, you can start it using the .Start () method and disable (raise an exception) using the .Abort () method, but this is a synchronous model (that is, we eat with a knife and fork, but we cannot cut it until we take it with a fork), but there is asynchronous (we eat and vacuum, and our legs at the same time do the bench press by +100500 approaches), which we took to use.



Here we wrote our first "pseudocode" and the main provisions / ideas of our project. The first stage has come to an end, as well as our mindlessness has come to an end, then we will develop functions and we will have to sweat.



Thank you so much:



lair , unsafePtr , vlreshet , domix32 , vadimturkov , myxo , norver for ideas and article edits.



Waiting for your wishes and ideas, and the power will come with you!

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



All Articles