Making a multiplayer game requires users to log into their account before playing. User accounts are essential for connecting players in the game and lobby. They also enable features like adding friends, chatting, viewing other players’ stats, and inviting users to game sessions.
Prerequisites
In this tutorial, we will use React 18 with React Router 6 for the frontend and Express for the Backend part.
Additionally, we’ll utilize our components: scrollable container and Grid .
Showcase Overview
In this example, we will create a multiplayer game UI featuring login and registration screens, and a friends list that shows their names, with options to add or remove friends.
The game server will handle API calls from the UI.
Note: When testing the sample, avoid entering sensitive information during registration. Passwords are stored as plain text in the database for testing purposes.
Source location
You can find the complete sample source within the ${Gameface package}/Samples/uiresources/UITutorials/MultiplayerUI directory.
src folder contains the UI source code.
api folder contains the Express server source code.
Refer to the README.md file in this directory for information on running the sample locally or previewing it without building or starting any processes.
Getting started - Backend
We will begin by setting up the backend using Express. To start, install the required modules:
Server index
The index.js file for our server will establish a connection to the database and start the server on port 3000. We have already set up a cloud MongoDB database and hardcoded the URL into the server index file.
Here, the frontend is expected to be built inside the ../dist folder so the server can serve it.
API routes
With the entry file for the server set up, we need to configure API routes. In this sample, we have user-related routes with the following entry points:
Middlewares
Certain routes are protected using middlewares such as auth, which verifies if a user is logged in, and idValidator, which checks if a valid user ID is present in the request parameters.
To authenticate a user, we check for an existing session and confirm that it has not expired. The session expiration date is stored in the database. Since Gameface does not store session cookies on the frontend, requests should include the session-id header containing the session ID generated by the backend and stored in Gameface’s localStorage.
This middleware validates whether the request from the frontend includes a valid MongoDB ID in the id parameter.
User database schema
After connecting to the database, we need to define the schema for the users table.
Once the schema is defined, we can use the model to access user data in the database.
Sessions database schema
To store user sessions when they log in, we need an additional table in the database with the following structure.
User controller
The User Controller manages requests and returns relevant responses by performing operations with the database.
For example, fetching user data is accomplished with the following code:
Attention needs to be given to how users can log in or log out of our game. For user login, the backend should receive the user’s email, password, and session data. As previously mentioned, we store the session-id in localStorage in Gameface since there are no cookies. The frontend must pass the session-id header when making a login request to check if the user is already logged in.
Once the session is checked, we need to validate the user’s email and password. If the database does not contain a user with the provided email and password, the login request is denied. If the credentials are correct, the user’s status is set to true, and the sessionId along with user data is returned so the frontend can recognize a successful login.
Logging out is straightforward: change the user’s status to false and delete the relevant session from the database.
For more information on other handlers, refer to /api/controllers/userController.js.
With this basic backend setup, we can proceed to the frontend.
Getting started - Frontend
For the frontend, we will use the React framework. To avoid going into extensive detail, you can open the entire project in ${Gameface package}/Samples/uiresources/UITutorials/MultiplayerUI and check the following:
README.md - Contains information on how to run the sample.
package.json - Includes scripts for building the frontend, starting the server, etc.
webpack.config.js - Contains the webpack configuration that bundles the frontend source.
src folder - Holds the frontend source code.
Now, let’s focus on some details about the frontend.
React router and routes
We want our sample to have three pages: Login, Register, and Home. For this, we can use React Router. The home screen will have sub-routes for the friends list and adding friends.
Here, the <ProtectedRoute> component acts as a wrapper to grant access to the home page if you are logged in. If not, you will be redirected to the login page.
The protected route checks for a logged-in user via the useAuth hook, which utilizes the AuthContext to provide user data.
To handle user data in localStorage, we use an additional hook, useLocalStorage. This hook stores or retrieves an item from localStorage based on the provided key. It also uses a default value if the key is not found in localStorage.
Making requests to the backend
As a frontend developer, you typically use the fetch method to make requests to the backend. However, fetch is not supported by Gameface, so you should use XMLHttpRequest instead. Since we’ll be making these requests frequently, it’s practical to create a reusable hook. Additionally, we need to send the session ID and user ID as headers with each request. To facilitate this, we can use other hooks like useLocalStorage within our custom useFetch hook to retrieve the user data.
In this hook, const [user, setUser] = useLocalStorage('user'); retrieves the user data.
If user data is stored in localStorage, we set the request headers after opening the xhr.
We also handle Unauthorized responses, ensuring that if the user is not authorized by the server, they are redirected to the login page to log in and try again.
Login and register pages
These pages are straightforward as they include a few input fields for user data. Since Gameface does not support the form element, we need to handle data submission manually. Here is an example for the login page, which is similar to the register page.
The register page follows a similar pattern. Here is a snippet for handling the submit request:
Home page
The home page contains a container with friends and a logout button. To display the subroutes defined for the home page, we use the Outlet component from React Router.
Friends list page
For the friends list page, we’ll use gameface-scrollable-container to enable scrolling through the list and gameface-grid to structure the list items. Our database schema stores an array of friend IDs for each user. Since this array doesn’t contain full friend information, we’ll fetch detailed data for each friend when needed.
Adding friends page
This page works similarly to the friends list page. It fetches all users from the database who are not the current user and are not already friends with the current user. These users are displayed in a list, and they can be added as friends by clicking the + button.