Editor Pong

Online Multiplayer Pong written as a Unity Editor extension


July - August 2022

Independent Project

In the summer of 2022, I was teaching Unity to high school students at the National High School Game Academy, a summer game design program at the Entertainment Technology Center at CMU. I dedicated one class to teaching my students about writing code to expand the Unity Editor, both with Custom Inspectors and custom Editor Windows. To demonstrate the power of Custom Inspectors, I wrote a script where you can play a game of Pong using slider elements and a constantly updating Inspector window.

However, just this script wasn’t enough for me. After taking a low-level computer networks class and my work designing a custom web server and application, I had been wanting to build an online game, and this seemed like the perfect opportunity. Using only the .NET Net and Sockets libraries and working in the context of Unity Editor Windows, I implemented a server application that listens for clients, performs simple matchmaking, and runs games of Pong. I also made the client application that contacts the server, waits in queue, and then provides a user input interface and displays the state of the game once connected to an opponent. What follows below is the story of the game’s development and some of the challenges I faced along the way.

Preliminaries (and why you shouldn't build a real-time online game in the Unity Editor)

I reworked the game to run in a custom editor window rather than a custom inspector for easier game instancing and started work on a server application. The server would run in a separate editor window and would fork listening and game loop threads when started. The first hurdle would be to simply establish a connection between a client instance and the server. I chose to use UDP sockets for communication primarily because none of the benefits of TCP were relevant here: reliable transmission due to out-of-order delivery is not relevant here since I fully expect all my game’s messages to fit well within one network MTU, and retransmissions aren’t helpful as there’s no use receiving a packet with well out-of-date game information. This also meant I could avoid the time and bandwidth overhead of establishing and maintaining a TCP connection. Using UDP for client registration and game-queue communication as well imposes a small amount of risk, but given this project’s small scope, I chose to avoid the complexity that would come from juggling both TCP and UDP connections. 

Setting up even a simple listening loop wasn’t as easy as I had hoped. Scripting through the Unity Editor API makes some things quite easy, like displaying live debug information or making a button to execute code when clicked, but makes other things, like quitting your application somewhat tricky. I quickly realized that I needed to fork threads to perform any asynchronous network tasks, but keeping track of those threads became somewhat tricky. Whenever you save changes to code, Unity notices it and will recompile and reload everything (including custom Editor Windows) immediately. This means that I lose reference to any threads that had previously existed, but Unity doesn’t automatically terminate them. And if an orphaned thread was bound to the network port I was trying to use, my only choice would be to restart all of Unity. I quickly learned to build failsafe “kill” buttons on the server and client windows to minimize orphaned threads and connections while developing.

Here a single Pong Client connects to the server hosted in the Pong Server window

Waiting for Godot Opponents

Once I got the server and client talking to each other, I started to design the communication protocols that they would use to establish a new game. The client would initiate a connection, and then (assuming the wait queue was empty) the server would put that client in the matchmaking queue and send a confirmation message. From there, the server would send periodic confirmation pings twice a second, which the client would have to respond to. If the server didn’t hear a response ping, or the client waited too long in matchmaking, either end could terminate the connection. 

Setting up this simple communication loop was much more tedious than I expected. The Unity Editor isn’t an amazing debugging environment, and (as always) deciphering print statements from two ends of the same connection can get very tricky. I started using Wireshark to record the data sent, which sped up my processes quite a bit. I also spent a lot of this time getting used to working with UDP sockets. I modeled the server architecture on a connection-based structure, which meant I was fighting at times to send packets over the correct socket to the correct user. Much of this difficulty would have been saved if I used more familiar TCP sockets, although I’m glad to have learned about working with UDP as well.

Verbose logging functionality allows for some debugging, but real time captures with Wireshark were much more helpful

Developing a Protocol

Once I had designed and implemented enough connection protocol to allow a thread to connect to the server and wait in the queue long enough to time out, my development sped up quite a lot. Having determined the coding patterns and basic Editor scripting logic, I was now limited by my own understanding of the communication protocols, which were mostly just vague understandings in my head at the time. My previous network programming experience was implementing IRC and TCP protocols, which helpfully come with pages and pages of explicit behavior descriptions – I soon realized that I needed something similar. I wrote out an entire flowchart of the server and client lifecycles, noting also which thread or Editor scripting hook each step takes place in – Unity provides a lot of places to run code from (Update, InspectorUpdate, OnInspectorGUI, …) and they all run at different frequencies or after different events – I needed to be very intentional about what came where.

Diagraming out my connection protocol sped up the rest of development 

Finishing the Job

With this flowchart in hand, the most feature-full parts of the application actually became the easiest to implement. The client-side application fell very neatly into a simple finite state machine with states Disconnected, InQueue, and InGame and transitions triggered by network responses. The in-game logic was surprisingly easy to implement – both the server and client applications use the same codebase to simulate the game. Clients update their own paddle position to the server whenever it changes, and the server pushes full game state updates 10 times a second. These updates are definitive – the game will “roll back” points that were scored locally if a server update says otherwise, overriding even local inputs if they weren’t received by the server in time. This is unlikely to cause many issues – the game runs slow enough that points usually won’t come down to network latency. 

Online Editor Pong Retrospective

I’m very happy with what I accomplished on this project – I set out to make an online multiplayer game that runs within the Unity Editor, and I created exactly that. In the process, I learned a lot about C# networking code, UDP sockets, and the difficulties of writing a real-time application in the Unity Editor. There are a couple of features I didn’t get around to including, including making the client “predict” their opponent’s inputs based on a calculated velocity for smoother motion. I also considered implementing NAT hole-punching to allow clients to play together without running the game on a separate server, but I chose not to re-architect the server application in the ways required to do that. If I were to re-do this project, I’d make the protocol flow-chart much earlier on, and consider a totally binary-based protocol for network efficiency. 

I have always believed in the power of implementing and understanding low-level engineering systems before relying on other libraries to perform things for me. With this project under my belt, I feel much more prepared to tackle any networked multiplayer games I work on in the future, whether I’m using a multiplayer library such as Photon or if I’m building a system from scratch.