Battle on Big Island

Battle on Big Island is a real-time multiplayer artillery shooter battle royale.

Battle on Big Island (or BoBI for short) was my project in my junior year at DigiPen. The game was developed over 26 weeks with a technical team of 10 programmers. In this project, I was solely in charge of writing the networking and almost all networked gameplay. This game was my first attempt at making a networked game in C++, let alone using low-level sockets. I only had a little bit of experience from my previous networking classes plus some experience writing netcode when I was in high school. My main goal was to learn the basics of low-level socket programming and to learn how to implement replication for a real-time video game.

Networking Basics

First, let us go over the very basics of what I used for this project. I used the Winsock2 API for the base net code and decided to go with TCP for the project. I decided that TCP was good enough because as stated previously, this was my first real attempt at doing low-level network programming. I thought UDP would take more time to develop and I wanted the networking to be up and running ASAP so others could start working on their systems with networking in mind. BoBI also only needed to work on LAN with ethernet connections, as that were the conditions our professors would grade us on, so I felt TCP would be completely fine. Lastly, I wanted to make a server-client based game rather than a peer to peer based game. This is mainly because our game was planned to support many players, making peer to peer a much worse choice, as then the player with the worst ping affects everyone rather than just that one player.

Networking Architecture

The first thing I did was set up helper functions to get around most of the super low-level socket API as I think it is pretty cumbersome to use on its own. I made a SocketAddress class that generates a sockaddr from a string containing the IP and the port. Then, I used this to create 2 more helper functions. I made a Connect function and a Listen function. Listen would be called on the server and called bind and initialize the server socket, and Connect would be called on the client side to connect to the server socket.

Next, I made the Packet class. Each packet has a PacketType enum to differentiate what kind of packet it is and how to handle it (ex: Position packet, Bullet packet, etc). Data is stored in a char array by copying the memory over to the char array. I used a templated function to add any type of item to a packet. After adding the item, an offset member in the packet would be increased by the size of the item added, so the next item would not overwrite the data.

Each packet is packed similarly to a stack. To fill the packet with data, you call the AddItem function. Once the packet is finished and sent to the other host, they can call the GetItem function. This will return the first item the first host added to the packet. For example, if I were to call AddItem on a, b, then c, GetItem would return a, b, then c.

Next, let’s talk about how I sent and received packets. Since sending and receiving packets with Winsock blocks, I had a few options to get around it. The first option was to multithread the program and let those threads block. This makes the program run faster and smoother, but also takes a lot more work and knowledge about multithreading. The second option was to just set the timeout window for the recv and send calls to 0. This would keep the program single threaded and easier to write, but could potentially make the program lag. In the end, I decided to go with multithreading. This was most likely not a great idea for our project to succeed, but I wanted to try this out because I felt it would give me a better understanding of how multithreaded network code is written.

At the beginning of the program, I spin up two threads: one for receiving packets, and one for sending them. Packets are received and put into a vector. The packets in this vector are then copied over and updated in the main client update loop. I use mutexes to keep the vector thread safe. I do the same with the send vector; whenever you want to send a packet, the packet is added to the send vector. Whenever the send vector is not empty, the mutex locks the send vector and sends the packets to its destination.

Network Gameplay

As stated previously, I had also done quite a bit of the networked gameplay for this game. Most of my work revolved around just implementing gameplay features with my previously described packet system. For example, one of the bits of replication code I wrote was to create a bullet. I would create a packet, add in the “Bullet” PacketType, then add the position and velocity vector of said bullet. Once passed to the server, the bullet would be created with these arguments and the same packet would be echoed to the rest of the clients. Most of the gameplay code was similar to this; there wasn’t much structure to the gameplay code as we weren’t entirely sure what we needed. Our team did not have any designers, so most of our design ideas were up in the air until we decided to implement it. This definitely hurt the quality of code, but we needed to really focus on getting the game features in before deadlines. The overall structure of the networking code I made took quite a while to implement, so a lot of my gameplay code is very simple. I hope to do more complex gameplay programming in the future. I will still dive into some of the details of how we replicated players and physics.

For this game, I wanted everything to be done on the server. This would ensure that no cheating occurred and it would be simple to program. In hindsight, I most likely should have just gone with a client side approach, as lagging in my current system makes the game feel bad, but with no experience I did not know. So, my first attempt to replicate players was to simulate the player movement and all bullet physics on the server, using the client side versions to verify their position and correct it if it was off. This did not work very well however, as our player movement physics were not deterministic, so I decided to have the clients send their position to the server. I kept the bullets on the server however, as they were much more accurate due to the server and client sharing the same velocity and starting position. The client and server would run the same bullet trajectory code, making them identical, so the client version would just be a visual representation for the player.

Previous
Previous

Beyond