Video Summary and Transcription
Real-Time Multiplayer Game with React Native case study of Quickflex game creation, gameplay overview, personal introduction, problems faced, and solutions implemented. Game flow overview, routing challenges with Expo Router, transition to React Navigation for stack-based routing. Challenges with tab-based routing, transition to stack-based routing with Expo Router, importance of file structure in Expo projects. Challenges with game state management, handling UI states, and transitioning to improved code structure. Lessons on state management, using routes, hooks, WebSockets, and deployment challenges. Introduction to Durable Objects for stateful serverless applications, using PartyServer for WebSocket servers, and optimization tips for game development.
1. Real-Time Multiplayer Game with React Native
Real-Time Multiplayer Game with React Native case study of Quickflex game creation, gameplay overview, personal introduction, problems faced, and solutions implemented.
Hey, everyone. Thank you for tuning in for my talk, Real-Time Multiplayer Game with React Native. This talk is mainly a case study of my experience of building a game called Quickflex. If you haven't heard about the game, I would highly encourage you to go ahead and download it from the app store. It's a fun little game, and let me show you. Now, this game can be played with a friend or you can play it all by yourself. In this video, I'm going to show you how it is played when you want to play it with a friend.
So, when you start the game, you get an option to create a room. You can create a room and share that code with a friend. Using that code, a friend can join the same game room. Once you both are in that room, a timer or a countdown gets started, and then the game loads. This is how the game really works. The person who is the fastest on clicking on that icon or the green button gets the score, and the position of this button changes as soon as someone clicks on it for both the players, and this is what makes the game fun.
Well, that was about the game, but let me introduce myself. My name is Harshal Agarwal, and I live in Berlin, Germany. I work as a developer educator at CloudFlare. If you are building anything on CloudFlare, I would love to know about that. Apart from my day-to-day job, I also love to build really cool projects in my free time, and QuickFlags was one such project. Let's move forward with the talk, and let me talk about the agenda of this talk. In this talk, I am going to talk about the problems that I faced when I was building this application. I will also share the solutions that I implemented. Now, please note, these solutions are not the best solutions. They are not the most efficient solutions, and for that reason, I am going to cover the efficient solutions in the next step section.
2. Game Flow and Routing Challenges
Game flow overview, routing challenges with Expo Router, transition to React Navigation for stack-based routing.
Now, let's go ahead and take a look at the game flow or the game architecture again. So, once you install the app and open it for the first time, you get this how-to-play screen. This screen has a carousel where you can scroll through and learn how the game works. If you skip, if you click on the skip button to close this, it takes you to our home screen. Now, this is the screen where the user can select which mode they want to play the game in. If they select the dual mode, they are taken to next to another screen where they can either create a new room or join an existing room. But if they click on the solo mode, the countdown timer starts for the game and they can start playing the game. And if the user again wants to see how to play the game, they can click on the how-to-play button and it takes them to the instructions again.
Now, if you notice over here, that is a bunch of routing happening here. And that brings me to my first problem of building the application was routing. Now, the development of this application was started almost a year back. At that point of time, Expo Router was still in beta. There were not a lot of resources available out there that would help me with some of the issues that I was facing. Now, these issues were not really major issues, but it was more of the educational resources that were unavailable at that time. But don't worry, if you are building an application using Expo Router today, there are tons of resources available now.
The Expo team has done an amazing job making sure that they have the best documentation for this, or not just the documentation, even video tutorials for that matter. So, the first problem with routing started when I initialized or bootstrapped an Expo project. And if you initialize an Expo project, it has a kind of a tab-based navigation. So, the navigation is configured by the tabs, which means that you need to have tabs for the options on your screen to make those things work. The tab group had a layout file, which contained this for you. So, if you click on the home title or the home icon on the tab, it would take you to the next route, and similar for the Explore button.
3. Tab-Based Routing Challenges
Challenges with tab-based routing, transition to stack-based routing with Expo Router, importance of file structure in Expo projects.
But if we look back again at the screens for my application, there is no tab. The user should be able to navigate to different routes by either clicking or based on some game logic. This was one of the first hurdles that I had. Due to the lack of proper documentation at that point of time, I really struggled to make this work. Fortunately, my exploration into Expo Router taught me that Expo Router is using React navigation under the hood. So, I navigated through React navigation's documentation and was able to come up with a solution.
So, instead of having a tab-based routing, I can now have a stack-based routing. So, I don't need tabs anymore. I could just have the stack component in there, a component in there, and things started to fall in place. The other major learning that I had with Expo Router was the file structure. The file structure in the Expo project matters a lot if you're using Expo Router because every file inside your app directory is a route. And if you don't want a route, make sure that that particular file stays outside of your app. So, this is what my application structure looks like now.
There is an app directory which contains only the routes that I need and the layout.tsx file basically contains the layout of my application. The components and the hooks stay outside of the app directory. Now, I am not going to dive deeper into the Expo Router, but it allows you to also have groups for your routes. You can also have protected routes and all those things. I would highly encourage you to go and check the documentation to better understand how you can use those particular things. Now that I had routing sort of figured out, the next major issue that I had with my application was managing the state.
4. Game State Management Challenges
Challenges with game state management, handling UI states, and transitioning to improved code structure.
Now, because this is a game, there were a lot of things. What should my game do when it starts or when it ends? How do I manage the countdown timer? What about the game countdown timer? Now, these are two different timers. The start countdown is the timer when both the players join the room, which is a five-second timer. While the game countdown timer is basically the whole time period for that game, which is roughly 20 to 30 seconds. I also needed to keep the state of where that green button was positioned and make sure that it gets updated quickly. If a player is playing the game solo or they're playing in multiplayer mode, there needs to be a state for that as well.
I also needed to have a state for score, because no game is fun without scores. It does not really matter if you are playing it by yourself or if you are playing with a friend. One thing I also implemented which, in retrospect, wasn't a really good solution was basically having states to manage my UI. To give you an example, I did not have proper routes or separate routes for the how to play screen or even the multiplayer game form. These were basically handled by the UI states, so whenever a user clicked on the how to play button, it would basically update a state and based on the state changes, I would show the screen. Again, not an efficient solution, but this is what I had back then.
How does this translate into code? The very first version where it was just single player with a simple game logic had this many states. This in itself is a lot. When I added the multiplayer functionality without having separate rooms, but just one single room for different players to join and play the game, the state that I had to manage increased. Now when there was a room code or separate rooms for each of those games, this became massive. No, I did not write this code. I was also just testing out different AI tools to help me build this game, basically learning how to write code. When I asked AI to do this for me, this is what it came out with. In the first glance, it did not feel right to me and I was also scared of messing up with this code because one little change would break my whole game logic and when I asked AI to fix it, it wasn't really able to do that.
5. State Management Lessons and WebSockets
Lessons on state management, using routes, hooks, WebSockets, and deployment challenges.
This kind of took me to my first lesson of state management is never to have these many states in your application. I started looking into what would be better ways to solve this. One of the solutions that I implemented was use routes for state management. Now this did not handle all the states because obviously it did not make sense, but what it helped me to do was separate out my logic. So now I had an index screen which would basically show the buttons for the player to select the mode, but now there was also a game screen which would handle the game logic. So just separating out the screens gave me a better experience of building this application and the amount of issues I ran into decreased a lot.
The next thing I implemented was combining the similar states into hooks and use those hooks for simpler interface. So I started using hooks to have the game state and even to handle the position of the element. These two solutions itself helped a lot in improving the state management for the game, but it was still a bit of a mess, especially when it came to the multiplayer part. This is where I started using WebSocket as a state management. I am not really familiar with the concept of using WebSocket to manage states, but for my use case this worked really well. Every message that was sent through or was received through the WebSocket had a data type and based on the data type the component received, it would perform a particular action. So for example, if the message that came in was a score update, it would update the position of the green button and also update the scores of both the players.
Talking about WebSockets, that was the third issue that I ran into. I mean, I did not really run into that issue, but let's think about how we would build a WebSocket server. You would have to first maybe learn a new language if you want to do it at scale, or you can just use something like Socket.io to build it with JavaScript or TypeScript. Once you have that, the second thing was figuring out how to have the logic of rooms, because I wanted only two players to be in one particular room and not all the players running one single room. That would have just ruined the experience of the game. So having rooms was another challenge with that. The third one was where do I deploy it and how do I handle the scaling. Now I am very optimistic when I build such projects. I always think about it getting used by millions of people, which in theory hasn't happened yet, but I always wanted to make sure that I had that kind of optimization or that kind of proof for my WebSocket server.
6. Durable Objects and WebSocket Implementation
Introduction to Durable Objects for stateful serverless applications, using PartyServer for WebSocket servers, and optimization tips for game development.
Now fortunately, working at Cloudflare got me introduced to this primitive called Durable Objects. Durable Objects allow you to build stateful serverless applications. Now, I know it sounds a bit weird when I say stateful serverless applications, but don't worry, I'm not going to get into it and explain it to you right now. What I want you to know is because Durable Objects help you build stateful serverless apps, they are the best place to build any kind of multiplayer application, AI agents, or any coordinated apps. And because they are serverless, I don't really have to also worry about the cost of the infrastructure. I only will be paying for the amount that it's getting used for, so that also gave me kind of a peace of mind.
Talking about the peace of mind, the developer experience with Durable Objects is even better because I just had to write a class and with that I would have a WebSocket server up and running. Now there are different ways you can do WebSocket servers in Durable Objects, and my favorite way is to use the package called PartyServer. PartyServer is an npm package created by Sunil Pai. This package basically extracts away all the nuances of Durable Objects and within a few lines of code, as you can see on the screen, you can have a WebSocket server up and running. So technically, this is all I needed to have a WebSocket server on my Durable Objects. Once I had this up and deployed, I just had to implement it on my client side, which was my application and I created again a hook for this, which was useWebSocket, and it was using a library called react-useWebSocket.
In this, I would simply connect it to my WebSocket server and have a room code, which was again unique for each and every gameplay. And that's it. That was the implementation for the WebSocket server as well as the client. Well, what I talked about and what I showed were not very efficient solutions, but they were the best solutions for me at that point of time. While looking at the code again for the application and thinking about optimization, I noticed a few things that I want to share with you all in this section of the talk. Now, if there's anything I want you to take away from this talk, is everything on this particular slide. If you have an application that is going to handle a lot of states, especially if you're building a game, start with state machines, and Xstate is the best way to do that.
Comments