Как создать веб-сервер для игры с мультиплеером бесплатно

Проблема
Предположим, что вы хотите создать браузерную игру используя нативный JS или игровой движок, который поддерживает Web-экспорт (Godot, Unity).
Если ваша игра имеет небольшой трафик, например такой как Дурак или Монополия, где действия игроков пошаговые, вы можете ограничиться использованием Long-Pooling'ом посылая запрос раз в несколько секунд. Но что делать, когда вам нужно знать информацию о других игроках чуть ли не каждую четверть секунды, а то и чаще?
Решение
Мы можем решить эту проблему передавая игровой трафик напрямую между пользователями, чтобы сервер участвовал только в подключении пользователей друг с другом. Для этого будем использовать WebRTC протокол, а если точнее библиотеку PeerJS, которая использует этот протокол.
Для игры с мультиплеером я решил сделать Пасьянс-Паук в котором, можно было бы помогать друг-другу.
В качестве платформы для хостинга будем использовать Render, эта платформа предоставляет 750 бесплатных часов в месяц, если у нас будет только 1 веб сервер, то мы можем бесконечно его держать активным.

Сам бэкенд будет на NodeJS, для запуска PeerJS сервера достаточно будет создать peer.js файл:
const express = require('express');
const app = express();
const ExpressPeerServer = require('peer').ExpressPeerServer;
const server = app.listen(3001);
const cors = require('cors');
app.use(cors());
peerServer = ExpressPeerServer(server);
app.use('/peerjs', peerServer); // на этот роут идет подключение по PeerJS
peerServer.on('connection', function(client) {
console.log('client connected: ' + client.getId());
});
peerServer.on('disconnect', function (client) {
console.log('client disconnected: ' + client.getId());
});
app.get('/ping', function(req, res) {
res.send('pong!');
});
// Вечный цикл, чтобы держать сервер активным
setInterval(() => {
fetch(`${your_render_server_name}.onrender.com/ping`, {
method: 'GET',
});
}, 480000);
И package.json:
{
"scripts": {
"peer": "node peer.js"
},
"dependencies": {
"cors": "^2.8.5",
"express": "^4.21.2",
"ioredis": "^5.4.2",
"peer": "^1.0.2"
}
}
Мы будем использовать ExpressPeerServer, вместо PeerServer чтобы позже можно было добавить роутеры на поиск/создание/удаление игр.
Теперь сам клиент, который будет использовать наша игра. Для подключения игрока к другому игроку нужно сначала подключиться к Peer серверу
peerJs = new Peer(peerId, { // peerId - Строка с уникальным ID
host: host, // сервер куда подключаться без указания протокола пример: server.onrender.com
path: '/peerjs', // роут peerjs, должен совпадать с сервером
secure: true,
});
И обработать события на подключение к нам
peerJs.on('open', function () {
console.log('connected to peer server');
});
peerJs.on('error', function (err) {
console.log(err);
});
peerJs.on('connection', function (conn) {
_initConn(conn); // обработка соединения другого игрока
});
peerJs.on('disconnected', function() {
peerJs.reconnect();
});
Чтобы подключится к другому игроку, который уже подключился к Peer серверу, нужно написать
const conn = peerJs.connect(`${other_player_peer_id}`);
_initConn(conn); // обработка соединения другого игрока
function _initConn(conn) {
conn.on('open', function () {
playerConnections[conn.peer] = conn;
});
conn.on('data', function (data) {
// обработать данные
});
conn.on('close', function () {
delete playerConnections[conn.peer];
});
}
Примечание: Если в нашей игре больше чем 2 игрока, при подключении игрока к хосту, ему нужно отправить peer_id других игроков, чтобы он подключился и к ним. Если вы собираетесь делать игру, где хост и является сервером, то требуется позаботиться о передаче данных между игроками.
Заключение
Теперь вы можете создать свой сервер для игры с мультиплеером, используя данный метод, но из минусов, так как сервер не обрабатывает данные пользователей вы не сможете проверять, что игрок не читер и не отправляет другим игрокам "плохие" данные. Я решаю данную проблему валидацией в самой игре.
Мою игру вы можете найти на Яндекс Игры или на itch.io.