Это правильная архитектура для нашей мобильной игры MMORPG?

14

В эти дни я пытаюсь разработать архитектуру новой мобильной игры MMORPG для моей компании. Эта игра похожа на Mafia Wars, iMobsters или RISK. Основная идея состоит в том, чтобы подготовить армию для сражения с противниками (онлайн-пользователями).

Хотя я ранее работал над несколькими мобильными приложениями, но это что-то новое для меня. После большой борьбы я придумал архитектуру, которая проиллюстрирована с помощью высокоуровневой блок-схемы:

Блок-схема высокого уровня

Мы решили использовать модель клиент-сервер. На сервере будет централизованная база данных. Каждый клиент будет иметь свою собственную локальную базу данных, которая будет синхронизироваться с сервером. Эта база данных действует как кэш для хранения вещей, которые часто не меняются, например, карты, продукты, инвентарь и т. Д.

С этой моделью я не уверен, как решить следующие проблемы:

  • Как лучше всего синхронизировать серверные и клиентские базы данных?
  • Должно ли событие быть сохранено в локальной БД перед его обновлением на сервере? Что если приложение по какой-то причине завершает работу перед сохранением изменений в централизованной БД?
  • Будут ли простые запросы HTTP служить цели синхронизации?
  • Как узнать, какие пользователи в настоящее время вошли в систему? (Один из способов может заключаться в том, чтобы клиент продолжал отправлять запрос на сервер через каждые x минут, чтобы уведомить его о том, что он активен. В противном случае считается, что клиент неактивен).
  • Достаточно ли валидации на стороне клиента? Если нет, как отменить действие, если сервер не проверяет что-то?

Я не уверен, является ли это эффективным решением и как оно будет масштабироваться. Я был бы очень признателен, если бы люди, которые уже работали над такими приложениями, могли поделиться своим опытом, который может помочь мне придумать что-то лучшее. Заранее спасибо.

Дополнительная информация:

Клиентская часть реализована в игровом движке C ++ под названием мармелад. Это кроссплатформенный игровой движок, который означает, что вы можете запускать свое приложение на всех основных мобильных ОС. Мы, конечно, можем добиться многопоточности, что также показано на моей блок-схеме. Я планирую использовать MySQL для сервера и SQLite для клиента.

Это не пошаговая игра, поэтому нет большого взаимодействия с другими игроками. Сервер предоставит список онлайн-игроков, и вы можете сразиться с ними, нажав кнопку боя, и после некоторой анимации результат будет объявлен.

Для синхронизации базы данных у меня есть два решения:

  1. Сохраните временную метку для каждой записи. Также следите за последним обновлением локальной БД. При синхронизации выбирайте только те строки, которые имеют большую временную метку, и отправляйте их в локальную БД. Сохраняйте флаг isDeleted для удаленных строк, чтобы каждое удаление просто действовало как обновление. Но у меня есть серьезные сомнения по поводу производительности, так как для каждого запроса на синхронизацию мы должны сканировать всю БД и искать обновленные строки.
  2. Другой метод может заключаться в ведении журнала каждой вставки или обновления, которые выполняются для пользователя. Когда клиентское приложение запрашивает синхронизацию, перейдите к этой таблице и выясните, какие строки какой таблицы были обновлены или вставлены. Как только эти строки успешно переданы клиенту, удалите этот журнал. Но потом я думаю о том, что произойдет, если пользователь использует другое устройство. Согласно таблице журналов все обновления были переданы для этого пользователя, но на самом деле это было сделано на другом устройстве. Таким образом, мы могли бы также отслеживать устройство. Реализация этого метода занимает больше времени, но не уверен, что он выполнил первый.
Umair
источник
2
Я бы взял оценку MsSQL и возился с ней - я не уверен, что MySQL предоставляет с точки зрения репликации, но MsSQL 2008 R2 дает вам НАГРУЗКУ методов репликации (5-6) на выбор. Просто мысль, не ненавидящая MySQL или что-то еще, но Microsoft действительно хороша в кластеризации.
Джонатан Дикинсон
1
@Джонатан Дикинсон: MySQL Cluster: P
Coyote
1
Также обратите внимание на PostgreSQL - версия 9 имеет технологию репликации, PostgreSQL имеет отличную многолетнюю репутацию, которая отлично справляется с тяжелыми нагрузками, многие разработчики кажутся гайками оптимизации, и, что лучше всего, с открытым исходным кодом и полностью бесплатны для любого использования. (в том числе коммерческий).
Рэндольф Ричардсон
Дело в том, что мы не можем использовать одну и ту же базу данных на стороне клиента и сервера. Я думаю, что наиболее подходящей БД для клиента будет SQLite, чтобы он не занимал много ресурсов, так как приложение будет работать на мобильных устройствах. В то время как человек, работающий над серверной частью, знает только MySQL, поэтому мы выбрали его. Также я слышал, что большинство MMO-игр используют базу данных MySQL.
Umair
1
@umair: Клиентская сторона может быть SQLite (например, на iPhone она уже доступна). Но вы можете просто сохранить нужные данные в файле (что в любом случае делает SQLite) и вообще не беспокоиться о клиентской БД, как если бы вы работали с приложением Javascript.
Койот

Ответы:

15

Если это не игра «в реальном времени» в том смысле, что игрокам не нужно видеть непосредственный результат действий другого игрока на игровой сцене, тогда вы должны быть в порядке с HTTP-запросами. Но имейте в виду, накладные расходы HTTP.

При этом использование HTTP не избавит вас от тщательной разработки протокола связи. Но если вы отвечаете как за сервер, так и за клиентскую часть, вам повезло, поскольку вы можете настроить протокол, когда вам это нужно.

Для синхронизации между главной и клиентской базами данных вы можете использовать любой транспортный протокол, к которому у вас есть доступ, HTTP или другие. Важной частью является логика синхронизации. Для простого подхода просто объедините с сервером все последние изменения, необходимые клиенту с момента последней отметки времени в клиентской БД. Примените это к клиентской БД и продолжайте с этим. Если у вас есть больше изменений на стороне клиента, загрузите их, если они все еще актуальны, в противном случае откажитесь.

Некоторые игры даже не используют локальную БД, они просто отслеживают статус, объединяя необходимую информацию с сервера при необходимости.

Если потеря локальных событий неприемлема, тогда да, вы должны иметь локальное хранилище и сохранять его как можно чаще. Вы можете попробовать сделать это перед каждой отправкой по сети.

Для проверки активных пользователей мы использовали эхо-запрос HTTP каждые 20 секунд в успешной игре ... Это значение сразу же возросло, поскольку серверы были перегружены :( Команда серверов не думала об успехе. Поэтому я бы сказал, что вам нужно добавить сообщение или какой-то специальный заголовок в вашем протоколе связи, который позволит вам перенастроить ваших клиентов (для пинговой балансировки нагрузки по частоте и других связанных с связью значений).

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

Мы использовали шаблон команды в нашей игре, чтобы обеспечить простой и эффективный откат неудачных или недействительных действий пользователя. Команды воспроизводились локально, отправлялись одноранговым узлам или транслировались на серверное сообщение, а затем применялись к одноранговым узлам или проверялись на сервере, в случае возникновения проблем команды не воспроизводились, и игровая сцена возвращалась в исходное состояние с уведомлением.

Использование HTTP не является плохой идеей, так как позже оно позволит вам очень легко интегрироваться с клиентами Flash или HTML5, оно гибкое, вы можете использовать любой тип языка сценариев сервера и с помощью базовых методов балансировки нагрузки добавить больше серверов позже без особых усилий. масштабировать свой бэкэнд.

У вас много дел, но это веселая работа ... Так что наслаждайтесь!

койот
источник
3
+1 для «HTML, вероятно, достаточно хорош», потому что это, вероятно, :).
Джонатан Дикинсон
1
@Coyote: спасибо, что уделили время и дали такой подробный ответ. Очень нравится ваша идея HTTP для поддержки других клиентов.
Umair
1
Да пребудет с тобой сила :)
Койот
5

Как лучше всего синхронизировать серверные и клиентские базы данных?

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

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

Должно ли событие быть сохранено в локальной БД перед его обновлением на сервере?

Нет. Что если сервер решит, что событие никогда не произошло? В любом случае клиент не должен принимать решения о событиях, потому что клиенту нельзя доверять.

Я заметил, что вы также говорите о локальной БД двумя способами: один для «вещей, которые часто не меняются» и два для событий. Они не должны находиться в одной и той же базе данных по указанным выше причинам - вы не хотите начинать пытаться объединять или различать отдельные строки данных в базах данных. Например, ссылочная целостность становится проблемой, когда у клиента есть ссылка на элемент, который вы решили удалить с сервера. Или, если клиент меняет строку, а сервер меняет строку, какое изменение имеет приоритет и почему?

Будут ли простые запросы HTTP служить цели синхронизации?

Да, если они довольно редки или малы. HTTP неэффективен по пропускной способности, так что имейте это в виду.

Как узнать, какие пользователи в настоящее время вошли в систему? (Один из способов может заключаться в том, чтобы клиент продолжал отправлять запрос на сервер через каждые x минут, чтобы уведомить его о том, что он активен. В противном случае считается, что клиент неактивен).

Если вы используете временный протокол, такой как HTTP, то это разумная идея. Когда сервер получает сообщение от клиента, вы можете обновить время «последнего просмотра» для этого клиента.

Достаточно ли валидации на стороне клиента? Если нет, как отменить действие, если сервер не проверяет что-то?

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

Kylotan
источник
1
Ты сделал это. Вы правы насчет синхронизации БД +1 Клиент никогда не должен менять БД самостоятельно ... Просто вызовите функции, которые запускаются на сервере, и обновите БД, используя игровую логику, а затем получите результаты.
Койот
@Kylotan: спасибо за указание на потоки в моей модели
umair
@Kylotan: «Самый простой способ - реализовать базу данных в виде единого файла, который вы можете передать. Если вы попытаетесь сравнить различия между базами данных, это будет болезненным делом, и я говорю по своему опыту». это означает, что все данные загружаются при каждом запуске приложения. Есть вещи, которые не будут меняться очень часто, и поэтому может быть неприемлемо загружать их каждый раз. Это также значительно увеличит время запуска приложения. Есть предположения?
Umair
Если данных мало, то проблема исчезнет. Я был бы удивлен, если бы ваша централизованная база данных статических данных была очень большой. Но в любом случае, если вы разделите статические данные на более мелкие части, вам нужно будет отправить только те, которые изменились с прошлого раза. Конечно, вы можете сделать это для каждого ряда, если хотите сохранить время модификации в каждом ряду. Обычно я предпочитаю иметь статические данные вне реляционной БД, чтобы можно было легко перезаписывать их.
Kylotan