Разработка сервера для многопользовательской игры с помощью nodejs и magx

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Многие разработчики начинают разработку многопользовательского онлайн сервера на основе библиотеки socket.io. Эта библиотека позволяет очень просто реализовать обмен данными между клиетом и сервером в реальном времени, но продумать и реализовать всю логику и интерфейс взаимодействия сервера с клиентами, а также архитектуру масштабирования и оптимизацию трафика все равно придется.


Я хочу рассказать про библиотеку magx, используя которую можно не задумываться о сетевой составляющей (коммуникации сервера и клиентов), a сразу сконцентрироваться на разработке логики игры и клиентского UI.


При разработке архитектуры многопользовательсой игры обычно рассматриваются 2 подхода: с авторитарным сервером и не-авторитарным (авторитарным клиентом). Оба эти подхода поддерживаются библиотекой magx. Начнем с более простого подхода — не-авторитарного.


Не-авторитарный сервер


Суть его в том, что сервер не контролирует результаты ввода каждого игрока. Клиенты самостоятельно отслеживают введенные игроком действия и игровую логику локально, после чего высылают результат определенного действия на сервер. После этого сервер синхронизирует все совершенные действия с игровым состоянием других клиентов.


Это легче реализовать с точки зрения архитектуры, так как сервер отвечает только лишь за коммуникацию между клиентами, не делая никаких дополнительных вычислений, которые делают клиенты.


С помощью библиотеки magx такой сервер можно реализовать всего в несколько строк кода:


import * as http from "http"
import { Server, RelayRoom } from "magx"

const server = http.createServer()
const magx = new Server(server)

magx.define("relay", RelayRoom)

// start server
const port = process.env.PORT || 3001
server.listen(port, () => {
  console.info(`Server started on http://localhost:${port}`)
})

Теперь, чтобы подключить клиентов к этому серверу и начать их взаимодействие, достаточно установить js библиотеку:


npm install --save magx-client


и подключить ее к проекту:


import { Client } from "magx-client"

Также можно воспользовать прямой ссылкой для использования в HTML:


<script src="https://cdn.jsdelivr.net/npm/magx-client@0.7.1/dist/magx.js"></script>

После подключиеня, всего несколько строк позволят настроить соединение и взаимодействие с сервером:


// authenticate to server
await client.authenticate()

// create or join room
const rooms = await client.getRooms("relay")
room = rooms.length 
  ? await client.joinRoom(rooms[0].id)
  : await client.createRoom("relay")

console.log("you joined room", name)

// handle state patches
room.onPatch((patch) => updateState(patch))

// handle state snapshot
room.onSnapshot((snapshot) => setState(snapshot))

// handle joined players
room.onMessage("player_join", (id) => console.log("player join", id))

// handle left players
room.onMessage("player_leave", (id) => console.log("player leave", id))

Детальный пример как построить взаимодействие между клиентами и не-авторитарным сервером описано в соответствующем примере в проекте magx-examples.


Авторитарный сервер


В авторитарном сервере обработка и выполнение всех действий, затрагивающих игровой процесс, применение правил игры и обработки ввода от игроков-клиентов осуществляется на стороне сервера.


Клиент не может самостоятельно вносить какие-либо изменения в состояние игры. Вместо этого, он отправляет серверу, что конкретно он хочет сделать, сервер обрабатывает этот запрос, вносит изменения в состояние игры и отправляет обновленное состояние клиентам.


При таком подходе логика на клиенте сводится к минимуму, клиент начинает отвечать только за отрисовку состояния игры и обработку действий игрока. Преимущество такого подхода в том, что он значительно усложняет клиентам возможность использования нечестных приёмов (cheating).


Разработка авторитарного сервера требует описания состояния игры и правил взаимодействия игрока с этим состоянием на стороне сервера. В архитектуре сервере magx предполагается, что вся логика реализуется комнате (worker). Каждая комната фактически является отдельным сервером, к которому подключаются клиенты.


Данные/состояние каждой комнаты изолировано и синхронизируется только с клиентам комнаты. Одним из важнейших элементов авторитарного сервера — это система управления состоянием. Mosx — рекомендованная система управления состоянием, но архитектуре magx нет зависимости на какую-либо систему, поэтому выбор всегда остается за вами.


Описание игрового состояния


Так как вся логика игры должна строиться на основе состояния, то первым делом необходимо его описать. С помощью mosx это сделать достаточно просто — необходимо создать классы для каждого типа объектов состояния и обернуть его декоратором @mx.Object, а перед каждым свойством, по которому необходимо отслеживать изменение состояния для синхронизации с клиентами необходимо поставить декоратор @mx. Давайте рассмотрим пример состояния с коллекцией игроков:


@mx.Object
export class Player {
  @mx public x = Math.floor(Math.random() * 400)
  @mx public y = Math.floor(Math.random() * 400)
}

@mx.Object
export class State {
  @mx public players = new Map<string, Player>()

  public createPlayer(id: string) {
    this.players.set(id, new Player())
  }

  public removePlayer(id: string) {
    this.players.delete(id)
  }

  public movePlayer(id: string, movement: any) {
    const player = this.players.get(id)
    if (!player) { return }
    player.x += movement.x ? movement.x * 10 : 0
    player.y += movement.y ? movement.y * 10 : 0
  }
}

Единственное ограничение, которое необходимо учесть при проектировании состояния — необходимость использования Map() вместо вложенных объектов. Массивы (Array) и все примитивные типы (number, string, boolean) могут быть использованы без ограничений.


Описание игровой комнаты


После описания состояния и логики его изменения необходимо описать логику взаимодействия с клиентами. Для этого необходимо создать класс комнаты и описать необходимые обработчики событий:


export class MosxStateRoom extends Room<State> {

  public createState(): any {
    // create state
    return new State()
  }

  public createPatchTracker(state: State) {
    // create state change tracker
    return Mosx.createTracker(state)
  }

  public onCreate(params: any) {
    console.log("MosxStateRoom created!", params)
  }

  public onMessage(client: Client, type: string, data: any) {
    if (type === "move") {
      console.log(`MosxStateRoom received message from ${client.id}`, data)
      this.state.movePlayer(client.id, data)
    }
  }

  public onJoin(client: Client, params: any) {
    console.log(`Player ${client.id} joined MosxStateRoom`, params)
    client.send("hello", "world")
    this.state.createPlayer(client.id)
  }

  public onLeave(client: Client) {
    this.state.removePlayer(client.id)
  }

  public onClose() {
    console.log("MosxStateRoom closed!")
  }
}

Регистрация комнаты на сервере


Последний шаг — зарегистрировать комнату и сервер готов.


const magx = new Server(server, params)

magx.define("mosx-state", MosxStateRoom)

Полный исходный код рассмотренного примера доступен в репозитарии magx-examples.


Почему стоит обратить внимание на этот проект?


В заключение хотел бы упомянуть о преимуществах используемых библиотек:


Mosx


  1. Простой и удобный способ описания состояния через декораторы @mx
  2. Возможность применять декоратор @mx к вычисляемым свойствам.
  3. Возможность создавать приватные объекты @mx.Object.private и приватные свойства @mx.private, с различным уровнем доступа для разных игроков.
  4. Динамически изменять доступ игроков к приватным объектам.
  5. Встроенный механизм объединения объектов в группы для удобного управления правами доступа к приватным данным
  6. Возможность делать копию состояния для каждого игрока
  7. Полная поддержка Typescript
  8. Минимум зависимостей (на библиотеки MobX и patchpack — для сжатия пакетов)

Magx


  1. Простой и понятный API.
  2. Широкие возможности по кастомизации компонент сервера:
    • Механизм коммуникации клиента с сервером (по умолчанию используется webockets)
    • База данных для хранения данных комнат и сессий пользователей (по умолчанию используется локальное хранилище)
    • Механизм коммуникации серверов при масштабировании (из коробки реализован механизм коммуникации в кластере)
    • Способ авторизации и верификации пользователей (по умолчанию используются локальный сессии)
  3. Возможность работать в кластере из коробки
  4. Встроенные комнаты: лобби и relay (для не авторитарного сервера)
  5. JS Библиотека Magx-client для работы с сервером
  6. Мониторинговая консоль magx-monitor для управления комнатами сервера, их клиентами и просмотр состояния
  7. Полная поддержка Typescript
  8. Минимум зависимостей (на библиотеку notepack.io — для уменьшения сетевого трафика)

Данный проект достаточно молодой и надеюсь внимание комьюнити поможет ему развиваться на много быстрее.

Источник: https://habr.com/ru/post/531898/


Интересные статьи

Интересные статьи

Среди множества инди-игр, выпущенных за последние 10 лет, одной из самых любимых для меня определённо является Journey. Благодаря своей потрясающей эстетике и красивому саундтреку Journey стала п...
В 1С Битрикс есть специальные сущности под названием “Информационные блоки, сокращенно (инфоблоки)“, я думаю каждый с ними знаком, но не каждый понимает, что это такое и для чего они нужны
Эта публикация написана после неоднократных обращений как клиентов, так и (к горести моей) партнеров. Темы обращений были разные, но причиной в итоге оказывался один и тот же сценарий, реализу...
Рис. 1. – Внешний вид 4-х ступенчатого термоакустического двигателя с бегущей волной В предыдущих статьях я писал о том, как построить двигатель Стирлинга без поршней, то есть о том, как пос...
С версии 12.0 в Bitrix Framework доступно создание резервных копий в автоматическом режиме. Задание параметров автоматического резервного копирования производится в Административной части на странице ...