среда, 26 октября 2011 г.

Проблемы распределенности

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

 

Но есть одна проблема. Даже не одна, но обо всем по порядку.

 

Разделение модели и рассчитываемых данных

Модель пересчитывается ежесекундно. Каждую секунду, каждая планета, луна, астероид или искуственный спутник немного перемещаются по своим орбитам. Корабли становятся ближе к своим целям и т.д. 2000 звездных систем, сотни (а может и тысячи) объектов в них. Не опухнет ли сервер раскидывать все эти данные по сотням(в мечтах - тысячам) клиентов? Много ли найдется желающих (и имеющих возможность) принимать каждую секунду несколько сотен килобайт данных? 

 

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

 

Клиентам должны транслироваться только изменения модели и текущее время. Конкретные координаты объектов в модель не входят и рассчитываются клиентом самостоятельно.

 

Передача обновлений модели

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

Короче при мизерном изменении, по сути формально меняется вся модель и нужно передавать ее целиком (все те же сотни килобайт). Или по крайней мере часть модели включающую все объекты входящие в изменившийся. Т.е. если переименовали луну - передаем объект луна, а в него входят все лунные постройки, спутники и т.д. - хотя они не менялись. А если учесть что постройки могут ссылаться на своих владельцев, которые нифига не входят в объект луна, а совсем из другой части игровой модели... А еще есть корабли, которые хоть и могут в данный момент находится на лунной орбите, но вообще-то тоже не к луне приписаны.

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

 

Либо на каждое возможное изменение нужно иметь свой вызов updateSomething, который будет как-то находить объект требующий изменения. Т.е. много-много мартышкина труда по кодированию всех этих методов. А потом их все надо отладить... а в сутках только 24 часа и нет готовой на все толпы исполнителей. Так что тоже не годится.

Т.е. нужно как-то все это делать по другому, иначе игра не будет сделана в сколько-нибудь обозримом будущем.

 

Обновление объекта не должно  распространяться на объекты лежащие выше, или ниже его в иерархии. Передается только сам объект и ссылки на принадлежащие ему (или как-то связанные с ним) объекты, но при этом не java-ссылки, которые требуют сериализации всех объектов на которые ссылаются, а ссылки "символьные". Которые позволяют найти эти объекты на стороне клиента. Что в итоге выходит

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

Добавим к этому, что в качестве формата сериализованных данных имеет смысл использовать JSON или что-то подобное (а стоит ли изобетать велосипед?) - это облегчит передачу их поверх HTTP. А еще это облегчит задачу изменения модели неигровым путем и проблему изменения версий.

 

Версии ПО и внесение изменений в модель

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

Значит модель должна храниться в текстовом виде, свободно читаться и редактироваться в обычном редакторе и не должна быть жестко связана с версией ПО.

 

Обнаружение обновлений

Предположим что что-то обновилось, сервер разослал уведомления клиентам, но что-то произошло, и какой-то из клиентов уведомления не получил. Связь прервалась, по причине пятен на солнце или еще какая засада. Клиент  - не знает что он должен был что-то получить. И не спросит. Серверу - накладно требовать от клиентов подтвержления и делать повторные передачи. Где выход?

Выход есть.

  • Все объекты должны иметь время последнего обновления.
  • Перед запросом данных от сервера клиент пробегает по карте объектов и находит самый свежий объект. Это время будет отправлено серверу
  • Сервер должен переслать все объекты имеющие более свежее время. Строго обязательно в хронологическом порядке.
  • Если объекты не влезают все в один ответ, то ждем следующего обращения. По собственной инициативе сервер ничего не передает - иначе возможны потери.

Последний пункт требует разъяснений.

Предположим мы передаем по 3 объекта за раз.

на клиенте у нас объект 1

на сервере 123456789

клиент сообщает серверу: 1

сервер посылает 234 567 89

567 - потерялись

на клиенте 123489

клиент сообщает серверу 9

про 567 он так и не узнает.

Комментариев нет:

Отправить комментарий