--- title: Развитие игровой конфигурации tags: scw, playkot --- # Развитие игровой конфигурации **TLDR**: - Переходим с C# на TypeScript - Переписываем GD Tool на TypeScript+Electron - Сборщик и валидатор тоже на TypeScript - Промоушены переносим в git - ГДД будут частично лежать в конфигах в git и экспортироваться в конфлюэнс - Переносим всю конфигурацию в базу данных - Трансформируем GD Tool в веб-сервис ## Проблематика Ранее мы собирали информацию от участников команды о том, с какими трудностями они сталкиваются при работе с нынешними игровыми конфигами. Собранные сведения перечислены [здесь](https://wiki.playkot.com/x/no2iBQ), кратко их можно записать так: - Сложно делать слияния (конфликты при git merge) - Ошибки из-за недостаточных проверок - Пересекающиеся идентификаторы игровых сущностей - Ошибки копипасты при переносе данных из/в Confluence - Нет сводных данных по запускам акций (нужно для аналитики) - Неудобство работы с графическими моделями (подбор домика) Для решения этих проблем было предложено [несколько решений](https://wiki.playkot.com/x/CY_iBQ) в разные года, из которых по сути сейчас работает только последний вариант - GD Tool. Кроме того есть еще несколько, возможно, не столь очевидных проблем: - Конфигурация промоушенов ссылается на сущности из конфигов, но при этом имеет свой собственный цикл жизни, хотя промоушены - это тоже часть игровой конфигурации. Более того, промоушены уже сейчас есть в фиде файлов, но при этом они находятся отдельно от остальных игровых конфигов. - Конфигурация очень разнородная. Что-то записано в виде xml-файлов, что-то в виде json, какая-то часть данных в Confluence, какая-то в google-таблицах, конфигурация разбросана по нескольким репозиториям и дублируется. Разнородность и виде данных, и в месте хранения создает дополнительные сложности в понимании связей, а также порождает ошибки при переносе данных. ## Варианты решений Попробуем для каждой проблемы рассмотреть возможные решения. ### Ошибки при слиянии файлов конфигов Ошибки возникают из-за того, что файлы с конфигами очень большие. Часто возникает ситуация, когда одновременно один и тот же файл правят разные геймдизайнеры. Что можно сделать: - Разбить файлы конфигов на более мелкие. Например, сделать по одному файлу для каждой сущности в конфиге. Так как git оперирует файлами, то чем меньше правок в файле от разных авторов, тем проще слияния. - Отказаться от хранения конфигов в виде файлов и от использования git. Перенести конфиги в базу данных, где сама база данных будет следить за одновременным доступом. Вариант [уже предлагался](https://wiki.playkot.com/x/CY_iBQ) RnD-командой, пункт 3. ### Пересекающиеся идентификаторы Исторически сложилось, что идентификаторы сущностей в файлах конфигов следуют друг за другом. Более того, есть последовательности, которые разделяются между собой. Например, билдинги от 5000, экшены от 8000. Сущности разные, но они становятся искусственно связаны между собой дополнительными ограничениями. Вдобавок, геймдизайнеры должны договариваться о том, кто какие идентификаторы создаст, чтобы не было пересечений. Что можно сделать: - Использовать уникальные идентификаторы, например GUID (вида `b0e370b7-9db7-46c9-bade-eb77f1a3cfa8`, мощность 128 бит). Вероятность пересечения таких идентификаторов крайне мала. Но в отличие от нынешних 5-значиных чисел идентификаторы GUID не такие читабельные. Вдобавок, это изменение типа данных для идентификатора: было число, будет строка. - Уникальные идентификаторы в виде достаточно больших чисел (вида `9007199254740991`\*). Теряется читабельность. Пересечения возможны, хотя и все равно мало вероятны. Это может быть временным решением для создания новых идентификаторов, для которых не нужно согласовывать порядок. - Использование арбитра, который следит за упорядочиванием. Вариант уже был предложен в виде [ID Control Service](https://wiki.playkot.com/x/JCxSAw). - Хранение конфигов в базе данных и делегирование базе данных задачи по выдаче новых идетификаторов. База данных будет самостоятельно следить за порядком и отсутствием пересечений. \* 53 бита - максимальное целое число для JavaScript, [MDN](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER) ### Ошибки из-за недостаточной валидации Малое количество правил валидации не покрывает многие случаи, которые можно автоматизировать. Что можно сделать: - Добавить больше проверки для схемы документов. - Добавить проверки для ссылок между документами. - Добавить проверки для целостности групп документов (например, что в группе сумма вероятностей должна быть равна 100%). - Автоматизировать запуск проверок на каждое изменение данных (в файлах и не только). ### Ошибки копипасты при работе с Confluence Ошибки возникают из-за того, что данные разделяются между файлами в репозитории и страницами в Confluence. В конфлюэнсе удобнее смотреть и читать, в репозитории данные, которые используются программно. Возникает двунаправленная связь: ``` Repository <=> Confluence ``` Связь не автоматизирована, перенос происходит вручную. Что можно сделать: - Оставить только одно направление в связи. Так как точка истины - это файлы конфигов в репозитории, то оставить только связь `Repository => Confluence`. - Автоматизировать эту связь, то есть сделать автоматическое добавление информации на страницы конфлюэнс. Решение может быть выражено в виде специального конфига, который описывает запуск акции и который собирает в себе ссылки на участвующие сущности (айтемы, экшены, домики). За счет механизма встраивания документов можно подтягивать информацию из других документов перед экспортом, что уменьшит дублирование. ### Отсутствие сводных данных по запуску акций Аналитики вручную собирают информацию о том, какие айтемы, домики, экшены участвовали в акции. Они используют для этого ГДД в конфлюэнсе. На самом деле этот пункт во многом пересекается с предыдущим о копипасте из конфлюэнса. Что можно сделать: - В конфигах завести специальные дополнительные конфиги, которые описывают запуск каждой акции. Эти дополнительные конфиги будут использовать и аналитики, и геймдизайнры (для экспорта в конфлюэнс). Дополнительно, такие дополнительные документы будут включены в систему валидации, что уменьшит ошибки. ### Промоушены отделены от остальной конфигурации Промоущены живут отдельно от основной конфигурации в базе данных. У них свой цикл жизни, хотя он очень сильно совпадает с циклом жизни остальной конфигурации. Что можно сделать: - Перенести промоушены в конфиги. При публикации конфигов записывать промоушены в базу данных. Включить промоушены в систему валидации. - В админке оставить оперативные инструменты для управления промоушенами: включение/выключение. ### Разнородность конфигурации Разнородность существующей конфигурации - процесс многолетней эволюции. Разные идеи, разные форматы. Разнородность усложняет понимание и обработку, усложняет проверку данных. Части конфигурации находятся в разных местах. - Основные данные [в клиентском репозитории](https://gitlab.playkot.com/supercity/client/-/tree/development-html5/bin/data) и их обновляемая копия в [репозитории с промежуточной сборкой](https://gitlab.playkot.com/supercity/sc-html5-project/-/tree/development-html5/supercity/assets/main/data) - Промоушены в базе данных и [в репозитории](https://gitlab.playkot.com/supercity/supercity_promo_configs/-/tree/development) - Шаблоны GT Tool для акций в его [репозитории](https://gitlab.playkot.com/supercity/sc-configs/-/tree/development/GDToolConfigEditor/templates) - Информация о запусках в конфлюэнс - Расчеты баланса в гугл-таблицах Что можно сделать: - Все данные, которые используются для работы с конфигами постепенно собирать в одном месте. - Постепенно приводить конфигурацию к одной структуре. Для обеспечения обратной совместимости с существующим кодом использовать специальные правила в сборщике. ## Целевое решение Основная задача, которую преследуют все изменения - упрощение работы участников команды. Чтобы гейм дизайнерам было проще заводить новые конфиги и настраивать запуски механик, QA-инженерам быть уверенными в правильности введенных данных, программистам - иметь понятную и непротиворечивую схему работы с данными в коде. Исходя из этого предлагаю в качестве целевого следующее решение: - Все конфиги будут приведены к единой структуре коллекций и документов. - Все конфиги будут перенесены в базу данных. - Для работы с этой базой данных будет использоваться отдельное веб-приложение. Плюсы центральной базы данных веб-приложения в том, что они решают обозначенные проблемы: - Нет проблемы со слиянием конфгиов, так как больше не используется хранение на основе git. - Нет проблемы с пересечением идентификаторов, так как база данных следит за их уникальностью автоматически. - Веб-приложение позволяет работать с любого устройства, не нужно устанавливать дополнительные программы, скачивать репозитории с кодом. - Также веб-приложение может содержать в себе логики и проверки, которые действуют одновременно для всех пользователей (в отличие от десктопного приложения, которое может проверить только действия текущего пользователя). Это целевое решение соответствует уже предлагаемому ранее RnD-командой в виде переноса конфигов в базу данных и разработке клиентской админки для управления ([требоания](https://wiki.playkot.com/x/4HvcAg), [история, пункт 3](https://wiki.playkot.com/x/CY_iBQ)). > Ранее я был большим приверженцем подхода, в котором хранение файлов в git остается фундаментом. Если смотреть с точки зрения технического специалиста, этот подход хорош: он обеспечивает версионирование, авторство, историю изменений. По истории можно найти кто и что делал. Вооружившись правильным инструментарием можно очень удобно и быстро работать. А править xml-файлы в своем любимом текстовом редакторе - сплошное удовольствие. [Моя позиция в виде презентации для SCM](https://hackmd.io/@anwinged/rkMfmdvNP). > > Но в то же время git накладывает большое ограничение: нужно знать как работать с системой, уметь решать в ней возникающие проблемы (скачивание кода, отправка кода, решение конфликтов, работа с ветками и т.д.). Нужно знать текстовые редакторы, уметь с ними правильно работать. Это объемные и сложные знания, которые, на мой взгляд, гейм дизайнеров больше отвлекают от работы, нежели помогают им. Поэтому сейчас я вижу целевое решение именно в виде веб-приложения. ## Детали реализации Создание отдельного полноценного веб-приложения - большая работа. Она может занять месяцы из-за большой подготовительной работы, которая потребуется. Поэтому революционный путь не применим. Но хорошо может работать путь эволюционный, когда изменения вносятся постепенно в текущую схему работы. Эволюционный путь предполагает мягкое и постепенное изменение структуры игровой конфигурации с сохранением обратной совместимости для плавной миграции клинского и серверного кода. Достичь этого можно, построив вокруг текущей конфигурации систему из трех компонентов: - Сборщик - собирает файлы в представление, которое используют клиентский и серверный код. - Валидатор - проверяет, что в собранных файлах нет противоречий и ошибок. - Редактор - упрощает редактирование файлов. Все три компонента опираются на единую абстракцию, которая представляет конфигурацию в виде коллекций документов. ### Формат конфигурации Общие правила для записи конфигурации следующие: - Вся конфигурация состоит из коллекций документов (сущностей) - Каждая сущность представляет собой один игровой объект: айтем, домик, экшен, промоушен т.д. - У каждой сущности есть идентификатор, уникальный внутри своей коллекции. - Коллекции и документы могут быть представлены в разных форматах (XML, JSON, etc). - Рядом с коллекцией находится вся связанная с ней информация: докуметация, правила валидации, схема документов и т.д. Многие конфиги уже соответствуют данному описанию. Но расположение в файловой системе все равно будет меняться, так как кроме одного файла с конфигурацией будут еще правила валидации. Возможные этапы изменения в структуре. Правила валидации и схема документов добавлены рядом с файлом конфигурации: ``` mapobjects.xml mapobjects-schema.js mapobjects-validation-rules.js ``` Коллекция представлена как директория, данные и правила валидации перенесены в нее: ``` mapobjects/ objects.xml schema.js validation-rules.js ``` Данные разбиты на отдельные файлы по идентификаторам, упрощаются мержи: ``` mapobjects/ objects/ 1.xml 2.xml 3.xml schema.js validation-rules.js ``` Данные преобразованы в стандартный формат JSON: ``` mapobjects/ objects/ 1.json 2.json 3.json schema.js validation-rules.js ``` Не смотря на то, какой формат используется, сборщик все соберет в один файл: ``` mapobjects.dat ``` Создание сущностей в виде отдельных файлов - это промежуточный этап перед переносом конфигов в базу данных. Вдобавок, сущности в отдельных файлах проще мержить с помощью git, будет меньше ошибок слияний. ### Cборщик Сборщик - это трансформер из одного представления в другое. Сборщик обеспечивает совместимость нового формата конфигов со старым. Новый формат означает, что структура файлов конфигов будет постепенно меняться к виду, который можно переложить в базу данных. Исходя их этого, к сборщику выдвигаются следующие требования: - Читать конфиги в форматах JSON и XML, из одного файла или из нескольких. - Записывать конфиги в виде файлов JSON, где один файл - одна коллекция. - Поддерживать обратную совместимость со старым форматом конфигурации. - Работать и в виде отдельного прилоложения, и встраиваться в другие (в валидатор и редактор). - Чтобы можно было упаковать в Docker и запускать в процессах CI/CD. Расширения, которые можно реализовать в сборщике. - _Встраивание документов_. Например, внутрь промоушена встроить конфигурацию мини-гейма. В исходниках конфигурации эти данные будут находиться в разных коллекциях, но при сборке для опимизации будут соединены в один документ. - _Проекции_. Одну и ту же коллекцию можно представить в разном виде. Например, коллекция айтемов и отдельно коллекция специальных айтемов. Такие проекции могут также послужить целям оптимизации. ### Валидатор Валидатор проверяет полученные от сборщика файлы. Важный момент при проектировании валидатора с том, а где же разместить правила валидации. Простой вариант - разместить их внутри валидатора, как это сделано сейчас в Валидаторе 2.0. Серьезное ограничение этого варианта в том, что читать и менять такие правила могут только разработчики. Более гибкий вариант - размещение правил валидации отдельно от самого валидатора. В таком виде правила могут меняться без изменения кода валидатора, в том числе тестировщиками. Этот вариант я считаю более подходящим. В каком виде записывать правила валидации? - Использовать конфигурацию в формате xml/json/yaml/etc. В таком виде конфигурация обычно получается многословной. - Использовать специальный язык (domain specific language, DSL). Позволит записывать правила гибко почти на естественном языке. Разницу в подходах можно понять на примерах. Допустим, нам нужно сделать две проверки. Что вложенное поле в объекте ссылается на существующий объект в другой коллекции и этот объект в другой коллекции должен быть определенного типа. С использованием конфигурации это могло бы выглядеть так: ```yaml # mapobject rules: - type: field_link scope: all field: needStepResources.resource[type='item'].itemId exists_in_collection: items collection_where: - field: type value: simple_item ``` И с использованием DSL на JavaScript ```js // mapobject collection() .rule(FOR_ALL_OBJECTS) .find("needStepResources.resource[type='item']") .field("itemId") .exist_in(collection("items").where("type", "simple_item")); ``` На более сложных примерах выразительность DSL будет еще более явной, в то время как читаемость конфигурации будет падать из-за изобретения все большего количества полей и все более сложных форматов. Идея про DSL в некотором виде уже была ранее: [раз](https://wiki.playkot.com/x/zyOCAg), [два](https://wiki.playkot.com/x/eSSCAg). Отсюда следуют требования: - Правила валидации находятся рядом с конфигурацией отдельно от самого валидатора. - Правила валидации записываются в виде DSL - Валидатор должен работать и в виде отдельного прилоложения, и встраиваться в другие (в редактор). - Валидатор можно упаковать в Docker и запускать в процессах CI/CD. Варианты встраиваемых интерпретируемых языков (распространенные, есть множество и более экзотичных вариантов): - Lua - распространенный вариант для встраивания, но на проекте не используется. - Python - тоже распространенный вариант, используется аналитиками, а нам в первую очередь нужна понятность для QA-инженеров, которые работают с веб-приложением. - С# - синтаксис может быть сложным в отдельных моментах. В проекте используется мало, для вспомогательных инструментов. Сам по себе это не скриптовый язык, но может быть встроен в приложения, которые тоже на писаны на платформе NET. - JavaScript - очень распространен. В проекте используется и в клиентском коде, и в серверном. С моей точки, зрения JavaScript тут будет оптимальным языком для встраивания. Он распространенный, экспертиза по нему есть у всех разработчиков. Порог входа на мой взгляд меньше, чем в другие предложенные варианты. Кроме того, он очень просто встраивается, если приложение написано на платформе NodeJS (пояснения, почему это важно, в разделе про редактор). ### Редактор Задача редактора - облегчить создание и изменение сущностей конфигурации. Сейчас эту задачу выполняет GD Tool. Если смотреть на конечную цель редактора, как на веб-сервис, то в нынешнем виде GD Tool не может эволюционным путем трансформирован в веб-сервис. Технология WinForms не предполагает работу в виде веб-компонентов. WinForms также не предполагает кросс-платформенность, пользователи MacOS и Linux не смогут в будущем использовать этот инструмент. Сам по себе C#, на котором написан GD Tool, может использоваться для веба только способами, которые все равно предполагают создание интерфейса заново: - C# как бекенд + отдельный фронт на JavaScript/TypeScript, весь UI придется создавать заново. - C# + компоненты Blazor (тоже от MS), весь UI придется создавать заново. Развивать GD Tool далее в нынешнем виде можно, но такое развитие не позволит эволюционно достичь цели и перейти на веб-сервис. Или совсем отказаться от перехода в веб, или переписать GD Tool на подходящую платформу. Альтернативный вариант: переписать GD Tool на TypeScript + Electron. Пока функциональности немного, это будет сделать проще. Преимущества стека TypeScript+Electron: - TypeScript на проекте является целевым языком для перевода клиентского кода. Использование его более широко будет стимулировать изучение и переход. - Компетенции по C# развиты слабее, чем по JS/TS. Кроме того, появление сложного инструментария на C# означает, что проекту нужны будут соответствующие специалисты. Из других команд: долго, найм: долго и дорого. - Electron позволит постепенно превратить приложение из десктопного в веб, так как внутри себя уже является веб-браузера и веб-сервера (chromium + nodejs). - JS/TS позволит проще интегрировать правила валидации на JS. - JS/TS позволит проще разрабатывать UI. Большое разнообразие фреймворков и библиотек. Требования к функциональности уже были написаны ранее при проработке варианта "Клиентская админка": - [Предварительные требования](https://wiki.playkot.com/pages/viewpage.action?pageId=55708525&focusedCommentId=55711975#comment-55711975) - [Описание функциональности](https://docs.google.com/document/d/17RL3qCTnyVZ7FLYz47MtsWJaNHEyeKkA96UI8j693g8/edit#heading=h.w3pu3kskjc2). ### Перенос промоушенов Промоушены являютс ячастью конфигурации. Они во ссылаются на игровые сущности. При этом у промоушенов сейчас другой жизненный цикл. Они редактируются и доставляются до прода другим способом, нежели остальные конфиги. Это порождает ошибки и сбои в игре. Промоушены будут перенесены к остальным конфигам, будет хранится в виде файлов. При деплое конфигов они могут заливаться в базу данных Mongo, чтобы работать так же, как и прежде. Понятно, что деплой конфигов не такой быстрый, как изменение их в базе данных. Для решения этого вопроса можно сделать следующее: - В админке оставить возможность управлять включением и выключением промоушенов. - Оптимизировать скорость деплоя конфигов, целевой показатель - 2-3 минуты. ## План работ #### Этап 0 - Выбрать стек для дальнейшего развития: C# или TypeScript. TypeScript позволит сделать все то же самое, что и C#, при этом легче превратить инструментарий в веб-сервис в дальнейшем. #### Этап 1 MVP сборки и валидации для определенного файла конфигов (например, mapobject). - Сделать сборщик, переписать с PHP. - Сделать валидатор и правила валидации. - Внести изменения в структуру файла конфига, чтобы понять, как эти изменения поддерживаются в сборщике и валидаторе. 2-3 недели. #### Этап 2 - Автоматизировать запуск этих правил в CI. - Наполнять конфиги правилами валидации (на все дальнейшие этапы). 1-2 недели. #### Этап 3 - Переработка архитектуры редактора или создание нового (в зависимости от выбранного стека). - Включить полученные сборщик и валидатор в редактор. 4-8 недель. #### Этап 4 - В конфигах сделать специальные сущности для описания новых айтемов и выгружать их в ГДД (то, что сейчас в ГДД представлено таблицами). Использовать эти же сущности для аналитики. 2-4 недели. #### Этап 5 - Перенести промоушены в конфиги и включить их в процесс деплоя конфигурации. 2-4 недели. #### Этап 6 К этому этапу работа с фундаментом и основными изменениями должна быть закончена, останется только постепенно все привести к виду, чтобы перевести конфиги в базу данных. - Интеграция в редактор других существующих инструментов (напр. PromoCreator, LangManager). - Развитие в редакторе инструментов для работы с конфигами, как с базой данных. В этом этапе можно будет рассмотреть целесообразность найма для дальнейшего развития инструментов для гейм-дизайнеров. Неопределенно, около 12-36 недель. #### Этап 7 - Добавление ролей и доступов в редактор для разграничения работы с базой данных. - Перевод всех конфигов в базу данных. 2-4 недели. #### Этап 8 - Превращение редактора в веб-сервис. 2-4 недели. Итого, 27-65 недель (7-16 месяцев). Оценка очень-очень примерная.