I felt a desire to do something I hadn't tried yet, and that was to make a multiplayer game over the network within a .NET Core console application. Because - why not? Programming allows us to create anything we can think of. Let's have a look at making our game, Packet Battle!
You likely won't have as much fun as these people, but games are as much as you put into them. Let's begin.
This code is the bare-minimum you need in order to create a local server that listens for connections on a given port, and saves information that is sent to it. A few things we should note however - AcceptTcpClient is a blocking call, which means that the program flow will stop once we reach this code and not continue executing until we accept a given connection on our IP and port we've assigned to the TcpListener.
Once we have accepted a connection, a TcpClient is created. We get the [network] stream from the TcpClient and read data from it, converting the bytes we received into a string. Finally, we stop listening for connections and end the program.
Note - if you do try to run two copies of the application locally, be sure to change the port on each instance to something different. I used 6000 and 6001 in the footage above but you can use others if those are being used for some reason.
Packet Battle! |
Overview
The format of this article is we will approach different aspects we need to solve in order to make Packet Battle, and at the very end I'll share the entire source you can run and play Packet Battle with your friends.
Playing video games with friends |
You likely won't have as much fun as these people, but games are as much as you put into them. Let's begin.
Defining "server" and "client"
Before we go further, it is essential to understand the difference between a server and a client. In the terms of a game, each player is typically a client that joins a server that hosts multiple clients. Examples of these types of games are MMOs, team-based shooters, or medieval simulators. The server is the mediator of all of the clients within the game.
The type of architecture we are building for Packet Battle is more similar to a P2P (peer-to-peer) architecture where each client also acts like a server and has equal control over the control over the game as a whole.
The P2P architecture |
Yes - each user who wants to play Packet Battle will be running the exact same code. This is similar architecture to P2P applications (including ones that got sued for copyright infringement; but we aren't going to get sued for this game do not worry).
Listening for network requests
The heart of this game is communication, specifically TCP communication. We can't have a network game without communicating to the other players, and the way we are going to do that is through TCP.
We could choose to use UDP, but we are not guaranteed our communication will reach the other person. We'll discuss the difference between TCP and UDP in a future post.
Taken from the official documentation, the simplest code we need to write in our console app to create a "server" is to use the TcpListener. Below is some sample code that does that:
// Create the TcpListener int port = 6000; string myIP = Dns.GetHostEntry(Dns.GetHostName()).AddressList[0].ToString(); TcpListener listener = new TcpListener(IPAddress.Parse(myIP), port); try { // Listen for connections listener.Start(); // Save information sent to our server string message = string.Empty; // AcceptTcpClient is a blocking method call using (TcpClient client = listener.AcceptTcpClient()) { using (NetworkStream ns = client.GetStream()) { // Create a buffer and read the message sent to our server byte[] buffer = new byte[1024]; int bytesRead = ns.Read(buffer, 0, buffer.Length); message = Encoding.ASCII.GetString(buffer, 0, bytesRead); } } } catch (Exception ex) { // Print errors Console.WriteLine(ex); } finally { // Stop listening for connections listener.Stop(); }
This code is the bare-minimum you need in order to create a local server that listens for connections on a given port, and saves information that is sent to it. A few things we should note however - AcceptTcpClient is a blocking call, which means that the program flow will stop once we reach this code and not continue executing until we accept a given connection on our IP and port we've assigned to the TcpListener.
Once we have accepted a connection, a TcpClient is created. We get the [network] stream from the TcpClient and read data from it, converting the bytes we received into a string. Finally, we stop listening for connections and end the program.
Sending data
In order for us to have a true multiplayer experience, you need to be able to play with other players. In our example, this requires us to send data to another user, and can be done with the following code below:
// Sending data to a server string theirIP = "54.11.182.14"; int theirPort = 6005; string data = "my data"; char[] charArray = data.ToCharArray(); using (TcpClient client = new TcpClient(theirIP, theirPort)) { using (NetworkStream ns = client.GetStream()) { byte[] byteMessage = Encoding.ASCII.GetBytes(charArray); ns.Write(byteMessage, 0, byteMessage.Length); } }
We create a new TcpClient with the user's IP and port, reference the [network] stream and send bytes to the stream. These bytes get picked up on the user's computer - and they react to however they need to with the data. Which reminds me...
The contract
How will the computers communicate with each other? It depends. The nature of the "communication" depends on the game. Will one client request if a server is available, and if the server is available, what types of messages will the server tell the client? If the client sees the server is available, how will it communicate back to the server that the client is ready? How will gameplay continue after this?
In order to answer these questions, it is essential to know how the game of Packet Battle will play, and for that I turn your attention to this high-render image of our gameplay:
Game flow |
Player 2 will choose a letter. This letter will be sent to Player 1 with 4 other random letters. Player 1 needs to choose what letter Player 2 picked. Depending if Player 1 chose correctly, they may get one point (otherwise nothing). Scores will be updated, and the same steps are played out in the opposite way.
These steps will continue looping until one player has 3 points, at which point the game will end.
Gameplay
See a sample of the new title sure to WOW all your friends and family in 2018. Packet Battle!!
Note - if you do try to run two copies of the application locally, be sure to change the port on each instance to something different. I used 6000 and 6001 in the footage above but you can use others if those are being used for some reason.
Source
You can pull all the source for the project here.
Take some extra time and look at how we used C# extension methods, interpolated strings, using static directive, Fisher-Yates algorithm and the C# out keyword. For extra homework, you can work to add the following features into Packet Battle that are not currently implemented:
- The ability to pass in a port value that your application uses (that is not hardcoded)
- Closing the TcpListener without using alt + f4 (maybe this?)
- Selecting the IP of the server from a list that is populated when searching for the game as a client instead of typing it in manually (this might be a good place to start)
- Allow the user to enter in an IPv4 or IPv6 address
I was diagnosed as HEPATITIS B carrier in 2013 with fibrosis of the
ReplyDeleteliver already present. I started on antiviral medications which
reduced the viral load initially. After a couple of years the virus
became resistant. I started on HEPATITIS B Herbal treatment from
ULTIMATE LIFE CLINIC (www.ultimatelifeclinic.com) in March, 2020. Their
treatment totally reversed the virus. I did another blood test after
the 6 months long treatment and tested negative to the virus. Amazing
treatment! This treatment is a breakthrough for all HBV carriers.