Введение в проблему. Общие вопросы архитектуры.
На каких принципах строится работа ММО в целом сейчас?
Когда мы говорим о разработке игры, первым делом на ум приходит графика и звук. Они реализуются, как правило, в специализированных игровых движках, которые:
- Либо выпускаются, как готовые решения, чтобы ими могли пользоваться разработчики со всей планеты;
- Либо игровые компании пишут для своих игр собственные самостоятельные решения.
Разнообразие технологий, которыми реализуется игровой клиент, впечатляет:
- Сейчас наиболее популярным в мире является движок Unity. На нем работает около 60% разработчиков в мире. Его в какой-то степени можно сравнить с 1С по проникновению, но в данном конкретном случае речь идет про всю планету.
- За ним идет Unreal Engine – очень старый и популярный игровой движок, на котором делаются различные высокотехнологичные проекты. Правда, за счет своей сложности он намного менее распространен.
- Также в игровой разработке используется не очень распространенный движок CryEngine, который, тем не менее, довольно активно применяют для крупных высокографических проектов.
- И еще буквально недавно появился дополнительно движок Defold – можно сказать, что это будущая замена Flash для браузеров, для легких казуальных развлечений. Его выпускает самый крупный издатель онлайн-игр на планете, поэтому он крайне удобен именно для того, чтобы делать простые игры – легкие, которые быстро грузятся, быстро играются.
- Кроме этого существует колоссальное количество частных решений, в основном, реализованных на языке C++.
А что у нас творится на сервере? Серверных технологий колоссально много.
- Исторически каждый берет свой велосипед и начинает его потихоньку собирать – колеса прикрутит, спицы в колеса поставит, раму как-то сварит. Сели и поехали – у кого как получилось.
- Тем не менее, постепенно выделяются решения и технологии, которые можно назвать типовыми. Первым в списке указан Photon – это решение для организации так называемых «комнатных серверов» онлайн-игр. Эти сервера реализуют исключительно логику сообщений с клиента на клиент. Все, чем они занимаются – это только транспортные сообщения, с помощью которых, в основном, передается положение игрока. Это – сложнейшая работа по предикции (по предсказанию поведения) – люди играют в совершенно разных сетевых условиях, у всех разный интернет.
Однако такие решения, как Photon и некоторые его конкуренты не занимаются игровой логикой – поэтому каждому программисту, который садится разрабатывать соответствующий проект, вооружившись выбранными стеком сетевых технологий, готовым движком и библиотеками, приходится писать всю игровую бизнес-логику самостоятельно. В том числе, и работу с базами данных. Наверняка многие из вас знают, что существует большое количество облачных баз данных, в том числе и специализированных, предназначенных для разработки игр.
- Зачинателем этого дела был Parse – продукт от Facebook, предназначенный для хранения произвольных знаний и выдачи их на клиент по первому требованию, по принципу «положили-забрали» – этого обычно хватает. Потом Facebook решил этот сервис закрыть, поскольку им понадобилось свою команду, которая работала над развитием Parse (это почти 100 с лишним человек) перебросить на другой проект, связанный с виртуальной реальностью.
- Соответственно, когда они закрыли Parse, в свет вышло несколько аналогичных решений. Например, сервис Playfab, который специализируется на играх,
- Есть также множество облачных решений, которые сейчас в основном специализируются на «интернете вещей», предлагая хранение данных для этих устройств, но также могут использоваться и для игр (собственно говоря, и используются).
Теперь мы переходим к главному вопросу, который возникает, когда мы видим тему доклада – причем тут 1С?
Объектная модель 1С:Предприятия для оперирования ММО игрой в концепции сервиса.
Ответ на это очевиден – объектная модель данных 1С:Предприятия просто гениальна, она позволяет реализовать в себе абсолютно все.
Архитектурно мы делим всю информацию об игровом проекте на две части:
- Привычные нам справочные данные;
- И то, что мы называем «Прогресс игрока» – это его текущее состояние.
На слайде я дополнительно выделил, что эти данные поступают на клиент в разные моменты времени:
- Справочные данные попадают на клиент как при его компиляции, так и в дальнейшем, при патчах, но изменяются они крайне редко. Причем, их изменяет не сам игрок – это могут делать только game-дизайнеры и разработчики игры.
- А прогресс игрока меняется постоянно в условиях жесткого реального времени. Самые топовые игры сейчас обмениваются с сервером 64 раза в секунду. Позиция одного игрока «бегает» и, соответственно, это все нужно просчитывать.
Концепция игровых сущностей как связки наборов данных по модели 1С:Предприятия 8.
Как мы реализовали архитектуру сущностей?
У нас есть игровая сущность, которую мы представили в качестве набора данных объектной модели 1С:Предприятия. В нашем конкретном случае – это:
- Справочники;
- Планы видов характеристик;
- И регистры сведений.
Комбинация видов этих трех составляющих образует игровую сущность – при этом используется несколько планов видов характеристик, несколько регистров сведений и несколько справочников.
Комбинация всех этих объектов обеспечивает построение гиперкуба, который представляет собой игровую сущность в полном ее описании. Получается, что с помощью объектов 1С:Предприятия мы можем описать абсолютно любую игровую сущность – все, что когда-либо приходило в голову, любой жанр описывается комбинацией этих параметров.
Концепция хранения сведений о прогрессе игрока построена на тех же принципах. Используются привычные нам объекты – это:
- Справочник;
- Регистр сведений;
- И регистр накопления.
С помощью комбинации этих объектов описывается прогресс игрока таким образом, что мы можем получить его значение:
- И в настоящий момент времени;
- И то, как он изменялся по оси времени.
Это – совершенно классическая учетная задача, когда нам интересно, что в определенное время делал игрок.
Концепция генерации платформенно-оптимизированного кода для работы с отраслевыми сущностями, используя 1С:Предприятие
Дальше мы применили следующий подход – на основании объектной модели данных 1С:Предприятие, в которой описывается игровой проект с его сущностями, мы генерируем платформенно-оптимизированный код для работы с отраслевыми сущностями уже в игровых движках. Другими словами, мы берем данные и генерируем на их основании:
- C++ код;
- C# код;
- Или JavaScript код.
Используя для этого несколько языкозависимых генераторов для различных движков. Каждый из них достаточно прост:
- Оранжевым цветом на слайде выделены понятия, которые от одного игрового движка к другому не меняются и реализуются непосредственно в самом языке. Если говорить конкретно про языки семейства Си, то везде получаются одни и те же классы.
- А серым цветом выделены сущности, которые для каждого игрового движка в отдельности уникальны. Это – интеграционные сущности, которые внесены в игру Game-дизайнером. В нашем случае, это:
- Менеджер игровых сущностей;
- Менеджер записей прогресса;
- И менеджер интерфейса. Под интерфейсом здесь понимается классический пользовательский интерфейс, поскольку игры – очень специфический софт: что увидишь, то и получишь. Нажал – получил, нажал – получил. Все должно быть очень четко и прозрачно, особенно в стратегиях.
Клиент-серверный обмен через HTTP API. GET для публичных сервисов, POST для приватных.
Как все это дальше попадает на клиента? Как вы понимаете, 1С у нас выступает в роли сервера, причем, сервера многоликого. Он является одновременно:
- Средством разработки;
- Средством оперирования;
- И средством бизнес-анализа того, что мы построили.
Сейчас это все у нас реализуется через HTTP API (HTTP-сервисы 1С:Предприятия). Есть:
- Сервисы публичные (через них можно получать данные о фанатской активности);
- И сервисы приватные.
Что значит «Приватные»? Это значит, они шифруются, причем шифруются хорошо. Мы используем как сам по себе HTTPS, так и мы дополнительно «солим», «перчим» хэш, потом все это дело передаем и там прикидываем, кто, когда, какую сессию должен был получить, и как эти данные отдать.
Приватные сервисы выдают свои данные:
- На клиент разработчика;
- И на клиент игрока.
Конечная цель – чтобы данные из базы получал ровно тот получатель, кому это положено по внутренним правам:
- На клиент разработчика данные поступают, как правило, все сразу.
- А на клиент игрока данные поступают маленькими порциями – в основном, только его прогресс. При этом используется другой характер выгрузки.
Если выражаться привычными для 1С-ников терминами, то для разработчика мы делаем целиком выгрузку первоначального образа периферийной информационной базы, а для игрока у нас выдаются только тикеты изменений его прогресса.
«Даешь джейсонизацию всей страны!» Отдельные замечания о сравнении штатной фабрики ЗаписатьJSON() с работой на чистом потоке ЗаписьJSON.
Это подразумевает достаточно высокую нагрузку.
В связи с этим я хотел бы ответить на вопрос: сможет ли 1С-клиент, выступая в роли сервера, обеспечивать миллион тонких клиентов? Скорее, даже «сверхтонких клиентов», поскольку так исторически сложилось, что мы передаем между игровым клиентом и сервером очень небольшие пакеты:
- Их может быть много;
- Они могут быть очень частые, либо, наоборот, редкие;
- Но они маленькие – в них содержится совсем небольшое количество информации, которая нам нужна.
Как мы отвечаем на этот вопрос?
Это возможно, используя JSON. Все наши форматы обмена данными – это JSON, JSON и еще раз JSON. Его реализация в платформе имеет определенные нюансы, о которых мы узнали по опыту – и этим я бы хотел с вами поделиться:
- Во-первых, мы применяем для скорости комбинацию из платформенного кэширования данных и ручного кэширования данных.
- В базе данных мы очень редко делаем запросы по ходу – они нужны только для перестроения менеджеров.
- В основном, при старте сеанса мы читаем справочные данные и, держа их в оперативной памяти, начинаем быстро-быстро отдавать на клиента.
- Отдаем мы их в JSON и используем при этом потоковый метод записи/чтения JSON.
Коллеги на Инфостарте, насколько я успел заметить, почему-то в основном предпочитают использовать стандартную фабрику ЗаписатьJSON/ПрочитатьJSON. Она удобна, думать не надо, но она очень медленная. Здесь на слайде приведен график в миллисекундах – там формировалось 4000 объектов. Правда, это данные для 8.3.6 – на 8.3.9 уже несколько другая картина, но пропорции, тем не менее, сильно не изменились. Если вы хотите сделать JSON быстрым, чтобы отдать информацию на сайт или еще куда-то, откройте, пожалуйста, поток и кладите в него данные вручную. Метод ЗаписатьJSON нужно использовать, только если у вас очень низкая нагрузка.
Как один запрос семь ответов прокормил или некоторые частности реализации кэширования
Дальше – «Наличность – наше все!» Кэширование, кэширование и еще раз кэширование.
У себя мы применяем следующую модель кэширования:
- Оптимизация на уровне сервера приложений плюс код самой конфигурации.
- Использование виртуальной машины 1С и ее особенности. Например, все справочные данные у нас однозначно кэшируются через «Фабрику запросов» (о ней чуть позже) – они поступают в оперативную память и находятся там, пока сеанс работает. Так как в 8.3.9 нам сейчас еще и переиспользование сеансов сделали, то мы сейчас фактически имеем Stateful server. Пока он включен – он может жить вечность.
Все, что отдается на клиент, мы полностью контролируем сами.
- Прогресс игрока, который основан на регистрах сведений и регистрах накоплений, также кэшируется в оперативную память, и его чтение производится оттуда.
- Но, как только нам требуется запись (то есть, произошло какое-то изменение прогресса), вызывается специальный менеджер работы с записью – он реализован внутри 1С:Предприятие, как процедура, и занимается тем, что по контексту пытается понять:
- Каким образом лучше всего установить блокировки;
- Каким образом лучше всего записать это пакетом в базу;
- И какими пакетами записывать это в базу.
Для чего нужны такие хитрости?
Изначальная суть проекта и его важнейшее технологическое требование заключалось в его геосетке: сервера приложений размещаются по всей планете, а база от них может находиться совсем в другом месте. Соответственно, мы не можем применять привычный всем Shared memory, и нам приходится общаться с MS SQL по TCP/IP. Поэтому мы должны очень вдумчиво контролировать:
- Что мы посылаем в базу;
- Как мы это получаем;
- И как мы можем получить это как можно быстрее.
Поэтому, чтобы лишний раз в базу не лезть, мы контролируем ее на уровне специального менеджера записей.
Фабрики генерации запросов к базе данных как реализация lean manufacturing для переработки информации
Продолжая эту тему, я бы хотел рассказать о нашем «ноу-хау» – мы его называем «Фабрика генерации запросов» к базе данных. Несмотря на то, что сервер у нас может находиться, условно говоря, в Калифорнии, а база данных от него будет находиться за половину планеты (где-нибудь в Северной Ирландии, где у Microsoft есть дата-центр), мы должны получать эти данные очень четко. А так как объектная модель внутри себя – это одно сплошное составное значение, где все переплетено со всем, то в каждый конкретный момент мы должны получить его не просто так, а наиболее оптимальным способом. Для этого мы используем оператор «ВЫРАЗИТЬ». Это – «наше все».
- Специальный менеджер, который определяет контекст ситуации, при старте анализирует базу, какие работы произведены Game-дизайнером, и отдает все это «Фабрике генерации запросов»
- Она, в свою очередь, делает оптимизированные запросы под текущее положение дел в конфигурации.
- Все результаты запросов кэшируются средствами виртуальной машины 1С напрямую (кэширование висит на вызове функции). И в таком состоянии они используются в текущей деятельности.
Это отрабатывает очень быстро, и даже если говорят, что 1С тормозит, у нас есть достаточно жесткое требование, чтобы она не тормозила – и она не тормозит.
Автоматическое проецирование частных реализаций модели данных в клиентские структуры на других языках программирования.
Еще немного о платформенной генерации.
Сейчас у нас все реализовано довольно просто:
- У нас нет игровой логики;
- Нет динамического транслирования в другие языки. Когда мы сделаем динамическое транслирование в другие языки, это будет «бомба» и фантастика, мы в этом случае сможем делать полностью нативный код, написав его изначально на 1С. Это – технологически возможно, мы над этим работаем.
- На настоящий момент мы в генераторе делаем только структуру данных без логики. Сейчас для нас основное – это C#, C++, потому что наиболее употребляемые языки. Когда мы в базе 1С построили «гиперкуб данных игровой сущности», он с помощью генератора в конечном итоге превращается в поле MyEntityProperty – в какой-нибудь из классов, который описывает соответствующую сущность либо ее набор.
- А соответствующий HTTP API (HTTP-сервис) каждый раз забирает эти данные из базы и заполняет их в соответствующие поля.
Все это происходит у нас более-менее автоматически. Один раз задал структуру генератору, и дальше уже можно менять, как угодно – он его сам подстроит.
Отработка серверно исполняемых событий игровой логики
Как мы сейчас решаем проблему с игровой логикой?
В основном, конечно же, спасибо серверным расширениям 1С – с ними сейчас можно делать совершенно все, что нам нужно – расширения логики, расширения кода.
Как это выглядит в нашем случае?
- Ввиду того, что внутри API у нас весь код на английском, он и в 1С у нас на английском, чтобы сторонним людям было более-менее привычно, чтобы можно было показать какому-нибудь американцу, и он не испугался кириллического кода. Поэтому мы его пишем на смеси «французского с нижегородским» – в API снаружи он называется по-английски, а внутри он может быть описан целиком по-русски (смотря кто писал).
- Внутри API у нас есть серверно-исполняемые события, которые определяются на основании объектной модели данных. Эти конкретные участки кода можно сравнить с подписками на события, но это не совсем подписки на события, это не платформенный механизм, это, скорее, заранее спроектированные места, куда можно инжектировать свои расширения. В этих расширениях, соответственно, можно пользоваться:
- Тем универсальным API, который мы реализовали;
- И использовать частные методы ситуации.
Что такое частный метод ситуации?
К примеру, у нас есть игровая ситуация – игроку нужно построить домик в деревне. У него есть ферма, и он на ней строит домик.
Когда с клиента отправляется запрос о том, что игрок собирается сделать, сервер начинает проверять, можно ли это сделать и как это сделать. В конечном итоге, он выдает решение, что да, это можно сделать и для этого нужно предпринять такие-то действия, и отправляет это менеджеру записи. И в тот момент, когда он определяет, что с этим можно сделать (это именно игровая ситуация), он где-то должен посмотреть:
- Хватает ли денег у игрока, к примеру;
- Доступен ли ему этот домик;
- Выполнил ли он соответствующие задания в квесте для этого домика;
- Еще какие-то условия.
И именно в этот момент мы вставляем серверное расширение, и эту логику можно менять как угодно – в том числе, вставляя туда авторитарные проверки о противопиратстве.
Наша реализация многоуровневой системы локализации
Итак, про локализацию.
Много кто сталкивался с тем, что с локализацией у 1С есть проблемы. Мы с этим тоже столкнулись, и у нас появилось собственное решение, которое я здесь впервые озвучиваю – до этого мы никогда о нем не рассказывали.
В контексте платформы мы рассматриваем языки программирования, как подвид естественных языков. Поэтому, если говорить об объектах платформы, для нас:
- C++ – это язык;
- JavaScript – это язык.
При этом они являются диалектами английского языка.
Дальше – с языками игроков. Мы понимаем, что языков игроков – сотни. У нас на планете, по-моему, около 5 тысяч языков, из них тех, которые более-менее актуальны (на которые надо переводить) – около 70-ти. Поэтому мы реализовали полностью свою систему локализации.
- За это у нас отвечает комбинация объектных данных 1С:Предприятие, которые при старте полностью прогружаются в специальные таблицы, хранящиеся в оперативной памяти в виртуальной машине. Локализация всегда стартует при сеансе.
- И клиент, когда он первоначально сообщает в запросе, какой же у него язык стоит в локали, получает ответ на соответствующем языке. Он получает свои объекты плюс локализация.
- У нас система локализации позволяет локализовать любой описанный объект – все это, опять же, вешается на составные типы. Локализуется любая строчка, на любом количестве языков. В локализацию включается даже служебное описание внутри работы команды – потому что бывают мультиязычные команды.
- И в итоге все это летит на клиента:
- Либо на разработчика;
- Либо на игрока.
Game Architect или как мы делаем глобальный SaaS стартап, используя 1С:Предприятие.
В конечном итоге, все, что мы делаем, называется Game Architect. Это – Saas-решение для того, чтобы можно было разрабатывать, оперировать, анализировать и управлять игровыми проектами. Это одновременно и про бизнес, и основа игровой логики. Это не отменяет того, что:
- Нужно брать Unity;
- Нужно брать Unreal;
- Работать с графикой;
- Работать с камерой;
- Работать с эффектами;
- Писать шейдеры;
- Все остальное.
Но там где про бизнес – там мы потихоньку это делаем.
Как мы вносим свой вклад в выход коллег на рынок экспорта своих работ и услуг
Вот уже 13 лет, как я программист 1С. Всю жизнь я работал над проектами по конфигурациям Бухгалтерия, Торговля, Зарплата. Последние три года я работаю еще и над этим проектом – параллельно Бухгалтерии, Торговле, Зарплате и всему остальному. Работаю не только я, у нас уже потихоньку сформировалась команда с разных уголков планеты.
У нас есть одна проблема – очень многие из нас хотели бы поработать на международном рынке, но мы пишем на 1С. Надо осваивать локализацию на другие языки, не только на английский, но и на остальные. Мы предлагаем использовать в этой нише 1С:Предприятие. Мы не предлагаем покорить весь бухгалтерский учет планеты, но покорение каких-то нишевых отраслей учета – возможно. Мы будем это делать с помощью нашего Game Architect.
Данная статья написана по итогам доклада, прочитанного на конференции INFOSTART EVENT 2016 DEVELOPER.