Caputchin
Разработка игры для маркетплейса

Манифест caputchin.json

caputchin.json в корне твоего репозитория это источник истины автора и индексатора для игры маркетплейса. Индексатор читает его на сервере, чтобы узнать идентичность игры, где живёт её бандл, какие пресеты она предлагает и как её реплеить. Он никогда не читается в браузере; SDK не загружает его в рантайме.

Эта страница это справочник полей. Для построения смотри Построй игру для маркетплейса; для публикации смотри Опубликуй в маркетплейс. Точные типы экспортируются из SDK как GameManifest (смотри справочник SDK).

Минимальная одиночная игра

{
  "terms_accepted": true,
  "license": "MIT",
  "marketplace": {
    "name": "Leaf Memory",
    "description": "Match pairs of tropical leaves before the timer runs out.",
    "preview": "preview.png"
  },
  "npm": "@your-org/leaf-memory",
  "entry": "dist/leaf-memory.js"
}

Поля верхнего уровня

ПолеОбязательноНазначение
terms_acceptedдля индексацииДолжно быть литеральным true, чтобы подтвердить, что ты принимаешь Условия отправки в маркетплейс. Любое другое значение (или отсутствие) выбрасывает манифест из индекса. Только-самостоятельно-размещаемые манифесты могут его опустить.
licenseдля индексацииИдентификатор или выражение SPDX, покрывающее твой код и встроенные ассеты. Должно вычисляться в одобренный идентификатор (смотри справочник ошибок публикации).
marketplaceдля индексацииПрисутствие это сигнал «индексируй меня». Отсутствие означает валидную самостоятельно размещаемую игру, которую маркетплейс игнорирует. Смотри Блок marketplace.
npmс entryКоордината npm-пакета, которую индексатор разрешает в URL jsDelivr.
entryс npmПуть к собранному бандлу относительно репозитория. entry, npm или оба должны присутствовать для запускаемой игры.
gamesдля коллекцийПути под-манифестов для обёртки коллекции. Взаимно исключающи с entry / npm. Смотри Коллекции.
runопциональноВыделенный headless-артефакт реплея. Смотри Артефакт run.
preferredопциональноПодсказки представления, которые хост МОЖЕТ учесть. Смотри Блок preferred.
locales / skins / configurationsопциональноБлоки пресетов, потребляемые SDK в iframe. Смотри Блоки пресетов.

Поля version нет: индексатор сам прибивает бандл к неизменному ref (опубликованная версия npm или разрешённый коммит SHA).

Блок marketplace

ПолеНазначение
nameЗаголовок карточки. Откатывается на имя репозитория (одиночная / обёртка) или имя leaf-каталога (ребёнок коллекции).
descriptionПодзаголовок карточки и тело страницы деталей.
previewПуть изображения (относительно корня репозитория) или абсолютный URL. Около 600x315, субъект по центру.
versionСтрока версии только для отображения на странице деталей. Опционально.
supportОбъявленные автором флаги совместимости (responsive, touch, accessible, audio, ...), никогда не проверяемые платформой; выводятся как фильтры и иконки карточки.
authorОпциональный { name?, url?, email? }. name / url рендерятся как подпись автора на странице деталей; email никогда не показывается и является opt-in для писем о сбое публикации. Все три подполя независимы.

Блок полностью опционален. Опусти name / url, и страница деталей просто не показывает подпись автора (владелец GitHub, показываемый в другом месте на странице, это отдельная, всегда присутствующая идентичность, а не откат для этой подписи). Опусти email, и ты не получаешь уведомлений о сбое публикации. Задавай только те подполя, что хочешь.

Указатели распространения

Индексатор прибивает бандл каждой игры к неизменному ref, чтобы сохранённый хеш целостности оставался валидным, и перерешает на каждом прогоне (ежедневный cron плюс ручное «Опубликовать или обновить»):

  • entry + npm разрешаются в cdn.jsdelivr.net/npm/<npm>@<resolved-version>/<entry>.
  • Только entry разрешается в cdn.jsdelivr.net/gh/<owner>/<repo>@<commit-sha>/<entry>.
  • Только npm разрешается в дефолтную точку входа пакета на закреплённой версии.

Нет пользовательского закрепления версии; чтобы заморозить сборку, пользователь размещает её сам через атрибут game-src виджета (пользовательская игра).

Артефакт run

По умолчанию самопроверка реплея запускает твой живой бандл entry. Когда твоя игра большая или основана на фреймворке либо WASM, поставь выделенный лёгкий headless-артефакт run вместо этого:

{
  "run": {
    "entry": "dist/run.js",
    "modules": [
      { "name": "sim.wasm", "type": "wasm", "path": "dist/sim.wasm" }
    ]
  }
}
ПолеЗначение
run.entryПуть к headless-бандлу run относительно репозитория (экспорт run контракта реплея).
run.modulesОпциональный массив записей модулей, которые run импортирует по name.

Ограничения, которые соблюдает индексатор

Они валидируются во время индексации; нарушение проваливает публикацию с manifest-error (смотри справочник ошибок публикации).

run.entry:

  • Должен быть JavaScript: базовое имя должно совпадать с [a-zA-Z0-9_-]+.js. Артефакт run всегда JS (WASM поставляется как модуль, ниже).
  • Должен быть чистым путём относительно репозитория: без ведущего слеша, без обхода .., без пробелов, без ? / #, без префикса scheme://.

Каждая запись run.modules[] это { name, type, path }:

  • name должно совпадать с [a-zA-Z0-9_-]+.(wasm|js), модуль это либо JS, либо WASM, ничего больше. name это спецификатор импорта, который использует entry.
  • type должен согласоваться с расширением: wasm для имени .wasm, js для имени .js.
  • name не должно быть зарезервированным именем (entry.js, artifact.js), которое хост реплея использует внутри.
  • name должно быть уникальным внутри массива (без дубликатов).
  • path должен быть чистым путём относительно репозитория (те же правила, что и у run.entry).
  • Не более 16 модулей.

Опусти run, чтобы реплеить живой entry напрямую. Смотри контракт реплея о том, что должен экспортировать артефакт.

Блок preferred

Опциональный блок preferred несёт подсказки представления. Каждый ключ совещательный: хост МОЖЕТ его учесть, и он никогда не перебивает явный атрибут встраивания.

{
  "preferred": { "width": 360, "height": 480, "layout": "modal" }
}

Адаптивная игра, которая должна занимать свой контейнер по умолчанию, может объявить "full" на любой оси:

{
  "preferred": { "width": "full", "height": 480 }
}
ПолеЗначение
width / heightПиксельный след или литеральное "full". Виджет применяет это, когда встраивание оставляет width / height незаданными (явное значение встраивания, включая full, побеждает вместо этого). Пиксельное значение задаёт iframe этот размер; "full" растягивает эту ось, чтобы заполнить родителя, тот же эффект, что у встраивания width="full". Опусти, чтобы откатиться на встроенный дефолтный след виджета.
layoutОболочка, которую виджет строит вокруг игры: inline, modal или fullscreen. Используется только когда встраивание оставляет layout незаданным (его дефолт auto). Порядок разрешения: атрибут layout встраивания, затем эта предпочтительная раскладка, затем inline.

Эти подсказки учитываются только для игр, которые платформа разрешает на сервере (игры маркетплейса или id игры, данный без ключа сайта). Самостоятельно размещённый бандл game-src, который платформа не может прочитать до монтирования, игнорирует и след, и подсказку раскладки.

Блоки пресетов

locales, skins и configurations каждый объявляют опциональную schema (типы и документацию на каждый ключ) плюс presets (именованные банки опций). Виджет разрешает выбор посетителя против них и вручает твоей игре уплощённый результат как ctx. Полный каталог типов полей это общий справочник схемы настройки; те же типы питают схему панели пользовательской игры.

Поле skins.schema может быть color, ассетом (image / audio / video) или скаляром (boolean, number, range, list), используя те же формы ограничений, что и configurations ({ "type": "range", "min": 0, "max": 24 }, ["dots","stripes"] и так далее). Скалярные значения скина разрешаются в своё типизированное значение в ctx.skin (number это настоящее число), ровно как значение конфигурации; значения цвета и ассета разрешаются в строки.

_theme пресета скина объявляет режим, в котором он работает: light, dark или any (опущение означает any). Пресет light или dark показывается только в этом режиме; пресет any читается на любом фоне и годен для обоих. Есть одно умолчание на режим. Пресет, помеченный _default: true, это умолчание для тех режимов, для которых он годен, так что умолчание any покрывает оба; когда несколько годных пресетов помечены умолчанием для режима, первый в порядке объявления его побеждает. Перечисли пресет, специфичный для режима, выше пресета any, чтобы дать этому режиму выделенный скин, пока пресет any проваливается на другой.

Сплит-файлы .caputchin/

Блоки пресетов (особенно полные наборы локалей) делают caputchin.json длинным. Перенеси любую ось в свой собственный файл под папкой .caputchin/, оставив манифест коротким:

caputchin.json
.caputchin/locales.json
.caputchin/skins.json
.caputchin/configurations.json

Объект верхнего уровня каждого файла является этим блоком оси ({ schema?, presets }). Все три опциональны. Старшинство это замена целой оси, caputchin.json побеждает: если ось объявлена и инлайн, и как файл, используется инлайновый блок, а файл игнорируется (публикация предупреждает, чтобы ты знал, что он мёртв). Держи каждую ось ровно в одном месте.

Коллекции

Репозиторий может поставлять несколько игр. Обёртка коллекции объявляет пути детей вместо entry / npm:

{
  "marketplace": { "name": "Caputchin Core Pack", "description": "The official pack." },
  "games": ["./packages/leaf-memory", "./packages/dino-runner"]
}

Каждый путь указывает на каталог-ребёнок, держащий свой собственный caputchin.json. Id детей это owner/repo/<leaf-dir>. Опусти блок marketplace обёртки, чтобы индексировать детей без страницы коллекции.

См. также

На этой странице