Chapter 3
After I got the basics of a few games programmed, I wanted to move on to making multiplayer games. In order to do that, the two tables needed to communicate with each other on a low latency basis. This is when I ran into one of my first major hurdles. The cleanest option would have been to use TCP/IP to communicate from one raspberry Pi to the other. I soon found out though that my choice of using Tkinter might make this problematic. In order to implement TCP/IP in python, you need to have a loop that listens for input. But Tkinter itself runs a blocking loop that inhibits easily implementing a second listening loop for TCP/IP. I had some options here, including multithreading, but pretty much all of this was already above my pay grade when it came to programming. Because of this I decided to explore a few (in my opinion) simpler options. What I ended up settling on in the end was actually two lanes of one way communication via saving and reading simple text files.
I discovered these things:
-Using pickle, you can save small text files in python very fast.
-If you have one Pi that is trying to read a file that the other pi is currently saving, it gives you an error, but its not anything game breaking.
-If you have both pis trying to save the same file at the same time, it gives you an error, which DOES break the game.
With this information, I started doing a little bit of testing. Machine 1 (I call it the “server” machine) has a very small text file that it is continuously saving. Machine 2 (I call “client”) is continuously trying to open and read this file. There is a separate file that machine 2 is continuously saving, and which machine 1 is reading. The reads were successful about ¾ of the time, with the other ¼ giving errors because the file was in the middle of being saved. If there is a read error, the software just keeps rolling on the most recent information it has, and tries reading again the next loop.
I really didn’t think this was going to work. It sounded SO hacky. But I wrote some test code, and was able to achieve an average of 20 to 25 successful reads per second. This equates to about 40 ms of lag (one way), and a total of about 80 ms of lag on round trip communications. In other words, if you’re specifically watching for it, the lag is barely perceivable, but it is not a hindrance to game play. Its worth noting that the files I’m using for communication are VERY small. I think one is about 100 bytes, and the other one is maybe 67 bytes.
I also had to figure out how to implement multiplayer games from a front end user interface standpoint. To explain how I did it, I probably need to back up for a second and describe my software a bit more. When the game first loads, there is a Main Menu that lists all of the games, with a short description of each game. It looks like this:
After your game is selected, pressing the start button one time enters what I call the “shell” screen. It looks like this:
This screen has a more in depth description of how to play the selected game. From this screen, you can press the start button to begin the game, or you can press either the up or down arrow to go back to the main menu. Oh I should mention the buttons I put on each table, they look like this:
As soon as the game is finished, the machine determines if you got a high score (It saves the 4 highest scores). If you did get a high score, it tells you that youre about to have your picture taken, then it takes your picture and adds it to the high score table. It then kicks you back to the game shell screen. If you didn’t get a high score, it sends you to the shell screen right away.
So that was the flow of my machines, and I wanted to implement multiplayer in a simple fashion. The way I ended up doing it was that any multiplayer game needs to be initiated from the “server” machine. On that machine, you press both the up and down arrows simultaneously to toggle between the single player main menu and the multiplayer main menu. Once you are in the multiplayer menu, a multiplayer game request can be sent to the client machine, assuming it is not in the middle of a single player game. If it is, then the server machine gets an error message saying the client is busy. Once a successful multiplayer request is sent, the client machine has the option to either accept or decline the game. If the request is accepted then both machines go to a multiplayer shell screen with a description of the game. From this screen, if either player hits their start button, the multiplayer game will start.
With this system implemented, I started designing some multiplayer games. I knew I wanted to make 1v1 vs games, but I also wanted to make Co-op games where both players were working together. The first few multiplayer games I made were fairly straightforward, the first of them being called “whack-a-hole”. This is a 60 second timed game where 2 holes are lit up on both boards at any given time. Whoever hit the hole first gets the point. Pretty straightforward. Here is a demo video:
From a processing standpoint, EVERYTHING occurs on the server machine. The client machine is merely reporting what holes on it have been hit. The server machine is doing all the logic and scoring, and then reports back to the client machine telling it what holes to have lit up and how bright to have them, and also what information it should be displaying on each of its on screen labels (time remaining, score, balls thrown, etc). This means that in the youtube link above, when the player on the right table (the client table) hits a hole, that machine saves the text file saying what hole was hit. This text file is then opened by the server machine, the data is read and processed, and the server machine then saves its own text file telling the client machine to turn off the lights for that hole. The client machine then opens this text file, reads it, and turns the lights for that hole off. Watching carefully, i'm not sure if there is any perceivable difference between how quickly the lights turn off on the client table vs how quick they turn off on the server machine. Thankfully computers are fast enough to make up for my hacky programming skills
At this point, I had a functioning communication protocol for my multiplayer games. Everything seemed great. But in chapter 4 I will start talking about a major problem that I encountered that almost derailed my entire project.