Источники, экспорт и отправка
Существует три пути записи, у каждого свой адресат:
- отражение записывает подготовленные изменения авторинга в источник.
- экспорт записывает значения запечённых SO обратно в файлы таблиц.
- отправка отправляет значения запечённых SO в живую Google-таблицу поячеечно.
Источники импорта
Источник импорта — это полноценный выбор в ассете настроек. Каждый источник объявляет собственную возможность авторинга (CanAuthor):
| Источник | Что читает | Авторинг (запись обратно) |
|---|---|---|
| LocalFile | Папка с файлами .tsv / .csv / .xlsx (только непосредственные дочерние элементы; один файл = одна вкладка, книги xlsx предоставляют свои листы) | Полный — отражение, правка структуры, переименование ключа/вкладки |
| GoogleSheet · SheetsApi | Приватная/расшаренная таблица через JWT-аутентификацию сервисного аккаунта (руководство по настройке) | Полный — точечная запись ячеек, перезапись структуры, отправка |
| GoogleSheet · ExportUrl | Таблица, расшаренная по ссылке, через её URL экспорта — аутентификация не нужна | Только для чтения (CanAuthor = false) — отправка/отражение/правка структуры/удаление отключены, с объяснением |
| Пользовательские провайдеры | Всё, что регистрирует плагин (ISheetSourceProvider — БД, REST, внутренние форматы) | На усмотрение провайдера через его флаг CanAuthor |
Примечания:
- ExportUrl требует карту gid (имя вкладки → значение
#gid=). URL экспорта без gid молча возвращает только первую вкладку, поэтому карта обязательна (GoogleSheetGidMapMissing, дублирующиеся gid отклоняются). Режим SheetsApi обнаруживает вкладки автоматически и не нуждается в карте. - Встроенные читатель/писатель xlsx написаны вручную поверх OOXML (только
System.IO.Compression+System.Xml— без NPOI/ClosedXML, ноль стороннего кода), поэтому он не добавляет никаких DLL, способных конфликтовать с другими ассетами в вашем проекте. Это один общий кодек: тот же читатель работает в редакторе Unity и — скомпилированный в WebAssembly — в веб-приложении, так что два хоста не могут разойтись во мнениях насчёт ячейки. Он намеренно минимален и честен в этом — только значения, без пересчёта:- Ячейка с формулой отдаёт значение, закешированное в файле. Формула без закешированного значения и ячейка с ошибкой (
#REF!,#DIV/0!) отклоняются (UnsupportedXlsxCell) — сохраните книгу один раз в Excel, чтобы закешировать значения, либо материализуйте формулы в значения. - Ячейка с форматом даты читается как дата и отображается как
yyyy-MM-dd— как ISO-тип ячейки, так и обычное число, чей стиль — формат даты, при этом учитываются обе системы дат, 1900 и 1904, — вместо необработанного порядкового номера, который хранит файл. Другие числовые форматы, объединённые ячейки и диаграммы не импортируются. - Эти трактовки — закешированные значения формул, даты как отображаемый текст, игнорирование форматирования — это неизменная политика читателя в обоих хостах, а диалог импорта веб-приложения дополнительно называет те из них, что реально сработали, в примечании «Как была прочитана эта книга».
- Табуляция или перенос строки внутри ячейки отклоняется (
UnsupportedCellCharacter) — используйте;для списков. - Коды типов ячеек, которые читатель не распознаёт, считываются как их необработанный текст, а не отклоняются.
- Ячейка с формулой отдаёт значение, закешированное в файле. Формула без закешированного значения и ячейка с ошибкой (
- Локальные файлы должны быть в Unicode. BOM UTF-8 или BOM UTF-16 (LE или BE) распознаётся; без BOM файл декодируется как строгий UTF-8. Устаревшая однобайтовая кодировка вроде CP949 или Shift-JIS отклоняется (
UnsupportedEncoding), а не угадывается. Угадывание декодировало бы по-разному на разных машинах и молча повреждало бы данные. Пересохраните файл в UTF-8. - Источник может вернуть частичный результат — один повреждённый файл не отбрасывает читаемые вкладки; проблемы приходят в виде диагностики.
- Пользовательские провайдеры источников обнаруживаются автоматически и появляются в том же выпадающем списке настроек — см. Создание плагинов.
- Студия сообщает, когда источник изменился без вашего ведома. При фокусировке окна — или по запросу из меню ⋯ — студия заново читает источник и сравнивает его со снимком вашего последнего импорта, показывая значок только тогда, когда данные действительно отличаются: повторно сохранённая или просто переформатированная таблица остаётся без изменений, потому что сравнение идёт по содержимому, а не по временным меткам. Клик по значку предлагает выполнить импорт; ничто не опрашивается по таймеру, ничто не импортируется само по себе, а офлайн-режим или отсутствие авторизации просто означают отсутствие значка. Это работает одинаково для любого типа источника — локальных файлов, таблиц по URL экспорта и Sheets API.
Экспорт — обратная половина round-trip
⋯ ▸ Выполнить экспорт на панели инструментов Data Studio записывает значения запечённых SO обратно в файлы таблиц.
- Структура берётся из baseline, значения — из SO. Экспорт подставляет текущие значения в снимок baseline структуры вашей таблицы. Строки маркеров, порядок столбцов, комментарии и написанный человеком текст сохраняются на 100%.
- Семантический round-trip значений: нормализация
1.0↔1допустима (значение идентично); для float используется кратчайший round-trip-формат; десятичный разделитель всегда.. - Форматы:
Tsv/Csv/Xlsx/Json/MatchSource— каждая вкладка возвращается в формат, из которого была импортирована; для вкладок из Google или неизвестного происхождения используется откат к Tsv.Json— это формат только для исходящих данных, рассчитанный на машины, а не на таблицы: один файл на вкладку, записи в виде объектов,int/float/boolкак настоящие числа и булевы значения JSON, а любое другое значение — ссылки, списки, цвета, кривые, пользовательские типы — в точном каноническом текстовом виде ячейки, который хранит таблица, поэтому сервер или внешний инструмент могут потреблять игровые данные, не разбирая текст таблицы. JSON не является источником импорта, а JSON-файл не несёт никакой структуры таблицы для round-trip — таблица остаётся канонической. TSV и CSV пишут по одному файлу на вкладку;Xlsxзаписывает каждую экспортированную вкладку в одну книгу (SheetForge.xlsx), каждую вкладку как отдельный лист в порядке вкладок, — книга — это формат, созданный для хранения нескольких листов, и то, что они держатся вместе, также позволяет ссылочным выпадающим спискам указывать между листами (ниже). ПриMatchSourceвкладки, происходящие из xlsx, собираются в ту же книгу, а остальные возвращаются в свои файлы. Имя листа, которое правила книги не могут вместить (слишком длинное или содержит запрещённый символ), корректируется и называется по имени в отчёте — никогда не переименовывается молча. - Актуальность обеспечивается принудительно: экспорт с устаревшим запеканием после изменения схемы завершается ошибкой
ExportSchemaMismatch. ЗапечённыйSchemaFingerprintдолжен совпадать с таковым у baseline, поэтому сначала выполните импорт. - Ссылки на ассеты экспортируются обратно в виде адресного текста, который использует таблица, — ключ, либо
parent[sub]для вложенного ассета; группа берётся из столбца, — никогда в виде GUID. Типизированные столбцы (AssetRef@Group<Type>) проходят round-trip тем же способом. ЗначенияColor,AnimationCurveиGradientвозвращаются в своей канонической текстовой форме (см. Синтаксис таблиц); кривая без ключей экспортируется как пустая ячейка, а цвет ограничивается диапазоном 0…1 (без HDR).
Отправка — поячеечная запись значений обратно в Google Таблицы
⋯ ▸ Отправить в Google Таблицу на панели инструментов Data Studio отправляет значения запечённых SO в живую таблицу, ячейку за ячейкой. Пункт меню отключён — с указанной причиной, — если только активный источник не Google Таблицы в режиме API. Она спроектирована так, чтобы никогда не повредить живую таблицу, которую редактирует кто-то другой.
Из этой цепочки следуют три гарантии:
- Ничего не отправляется без вашего подтверждения поячеечного плана.
- Ячейка, изменившаяся в живой таблице после вашего импорта, пропускается, а не перезаписывается.
- Удаление строки отправляется, только когда живая таблица всё ещё показывает этот ключ именно в этой строке — всё, что сместилось, пропускается с уведомлением и никогда не угадывается.
Цепочка защитных мер по порядку:
- Требуются учётные данные SheetsApi — отправка в режиме ExportUrl отклоняется ещё до какого-либо сетевого вызова (
GooglePushRequiresSheetsApi). - Для каждой отправляемой вкладки требуется ключевой столбец — отправка заново находит каждую строку по ключу в живой таблице. Именно так она обнаруживает переместившуюся строку и безопасно пропускает эту запись, никогда не отправляя её не в ту строку. Вкладка без ключа с изменениями отклоняется (
PushKeylessTabUnsupported). - План + подтверждение — поячеечный diff (baseline против текущего SO) вычисляется как план: записи, добавления, удаления строк. План показывается для явного подтверждения, прежде чем что-либо будет отправлено; удаления стоят в собственном разделе, и каждое именуется по ключу, который исчезнет. Отказ = ни одна ячейка не отправляется.
- Повторное получение живых данных перед отправкой: непосредственно перед отправкой живая таблица заново загружается и сравнивается. Конфликтующие ячейки пропускаются, а не перезаписываются (сообщаются как предупреждения):
PushConflictCellChanged— эту ячейку отредактировал кто-то третий.PushConflictRowMoved— ключ найден в другой строке, чем видел ваш импорт, поэтому запись пропускается (никогда не отправляется не в ту строку). Выполните повторный импорт для повторной синхронизации, затем отправьте снова.PushConflictRowMissing— строка была удалена извне.PushConflictDuplicateLiveKey/PushConflictAppendKeyExists— неоднозначные цели.
- Удаления строк сопоставляются по ключу перед отправкой. Удалённая вами запись убирается из живой таблицы только после того, как повторное получение перед отправкой подтвердит, что её ключ всё ещё находится точно в той строке, которую видел ваш импорт: строка, которой уже нет, считается выполненной (повторная отправка не удаляет ничего дважды), а ключ, найденный в другой строке, — таблица сместилась, — пропускается с уведомлением и никогда не удаляется по позиции. Удаления отправляются последними, снизу вверх внутри каждой вкладки, поэтому более ранние удаления не могут сдвинуть координаты более поздних. Источник, который не умеет удалять строки (пользовательский провайдер без этой возможности), честно откатывается к старому поведению: удаление сообщается, а живая строка остаётся для вас.
После отправки проверьте в отчёте количество применённых/пропущенных ячеек. Если ячейки были пропущены, выполните повторный импорт для согласования и отправьте снова.
Изменения структуры в Google
Правки структуры (столбцы, маркеры, изменение порядка, переименования) для источника Google перезаписывают всю целевую вкладку целиком. Сначала выполняется проверка diff на живых данных, а перед перезаписью всего, что изменилось в таблице после вашего последнего импорта, требуется явное подтверждение. Правки значений остаются точечными (по ячейкам); путь перезаписи используется только для структуры.
Регистрации в Addressables, которые выполняет отражение
Перетаскивание ассета на ячейку AssetRef@Group в Data Studio — или выбор его из проекта — может подготовить изменение не только для таблицы, но и для проекта: добавление ассета в группу, перемещение его из другой группы или создание группы. Эти регистрации — часть отражения и выполняются в фиксированном месте цепочки — том же самом для локальной папки, Google-таблицы и пользовательского провайдера источника:
- Предварительная проверка проверяет всё спроецированное состояние так, как если бы подготовленные регистрации уже были применены, поэтому ячейка, указывающая на ещё не зарегистрированный ассет, не считается ошибкой.
- Таблица записывается. Если запись отменена или завершилась неудачей, ничего из перечисленного ниже не выполняется: настройки Addressables остаются нетронутыми, а регистрации остаются подготовленными для следующей попытки. Отражение, которое не смогло записать ни одной вкладки, потому что все затронутые вкладки были пропущены (например, когда были затронуты только вкладки, происходящие из книги), тоже их не выполняет. Отражение, которому вообще нечего записывать в таблицу — единственное подготовленное изменение это регистрация, — их выполняет и делает повторный импорт; никакая другая подготовленная правка этим проходом не фиксируется, поэтому она остаётся отменяемой.
- Регистрации выполняются по порядку: сначала создаются группы (со схемами по умолчанию
BundledAssetGroupSchemaиContentUpdateGroupSchema), затем записи добавляются или перемещаются и получают свой адрес, а настройки сохраняются один раз. Каждый элемент заново проверяется непосредственно перед выполнением и пропускается, а не применяется принудительно — когда ассет к этому моменту уже удалён, когда адрес теперь занят другим ассетом в этой группе, когда группу не удалось создать или найти, и когда на адрес больше не ссылается ни одна ячейка (регистрация никогда не создаёт запись, на которую ничто не указывает, а группа, у которой пропущены все записи, тоже не создаётся). Если в проекте ещё нет ассета настроек Addressables, он создаётся специально для этого. - Подготовленный список очищается — как применённые, так и пропущенные элементы, — а следом идёт автоматический повторный импорт, чтобы запекание увидело новые записи. Поэтому пропущенная регистрация честно сообщается при этом повторном импорте как
UnknownAssetKeyна ячейке, которой она требовалась.
Консоль показывает одну строку на каждый исход — Addressables: 'address' → group 'Group' для каждого применённого элемента, Addressables: skipped 'address' (reason) в виде предупреждения для каждого пропущенного, — и итоговую строку Addressables: N registered, M skipped. Для источника «локальная папка» диалог завершения отражения заканчивается той же итоговой строкой.
Выпадающие списки, записываемые в таблицу
Столбцы с конечным набором вариантов получают прикреплённое к таблице правило проверки данных, так что человек, редактирующий в Google Таблицах или Excel, выбирает из списка, а не запоминает написание. Ничего не нужно включать отдельно: правила вычисляются при каждом экспорте, отправке и записи авторинга обратно, и применяются везде, где цель может их нести.
| Столбец | Правило |
|---|---|
Скаляр Enum<T> | Фиксированный список членов этого enum. |
Скалярная ссылка (RecordId@Tab, а также пользовательский тип с паритетом ссылок — §4.4a) | Диапазон по ключевому столбцу целевой вкладки, оставленный открытым с конца, так что записи, добавленные в целевую вкладку, сами присоединяются к списку. |
List<>, столбцы-wrapper'ы, сам ключевой столбец | Без правила — там одна ячейка несёт несколько значений, либо нет цели для списка. |
- Подсказка, а не принуждение. Каждое правило нестрогое (Google
strict:false, xlsxshowErrorMessage="0"): значение вне списка помечается предупреждающим маркером, но всё равно принимается. Жёсткий отказ сломал бы обычный рабочий процесс «сначала записать ссылку, потом определить запись». Он также вступил бы в противоречие с собственными предложениями ближайшего совпадения импорта. - Правила — это метаданные отображения, а не значения. Они никогда не появляются в ячейке, поэтому round-trip не затрагивается. Экспорт без правил побайтово идентичен тому, что производился до их появления.
- Применяются независимо от значений. Прикрепление правил — это отдельный шаг, а не побочный эффект записи ячеек. Самый частый сценарий — добавить член enum, не меняя данные — отправляет ноль ячеек, поэтому побочный эффект никогда бы не сработал. Прикрепление идемпотентно, так что повторный запуск ничего не меняет.
- Неудача — это предупреждение, а не проваленная отправка. Если значения ушли, а прикрепить удалось только не все правила, отправка всё равно считается успешной; запустите её снова, и переприменятся только правила.
Что может нести каждый формат:
| Цель | Механизм | Заметки |
|---|---|---|
| Google Таблицы (отправка / запись обратно) | setDataValidation, объединённые в один запрос | Оба вида правил. Диапазон ссылки не имеет конечной строки, поэтому следует за ростом целевой вкладки. |
| xlsx (экспорт) | dataValidations после данных таблицы | Оба вида правил. Поскольку экспорт — это одна книга, диапазон ссылки указывает на ключевой столбец целевого листа внутри того же файла, открытый вниз по листу, — то же значение, что несёт диапазон Google. Правило всё же пропускается — и называется в предупреждении — в трёх честных случаях: член списка содержит запятую (встроенный разделитель разбил бы его), встроенный список превышает лимит спецификации в 255 символов (именно весь список в кавычках ограничивает формат), либо диапазон, чья целевая вкладка не входит в книгу. |
| TSV / CSV (экспорт) | — | Простому тексту некуда их поместить. |
| JSON (экспорт) | — | Файл данных, а не таблица — там нет ячейки, к которой можно прикрепить выпадающий список. |
Всё оставшееся честно сообщается как одно предупреждение DropdownNotSupportedByFormat за запуск, с указанием каждого затронутого столбца. Поэтому ответ на вопрос «почему в Google есть выпадающие списки, а в моём файле нет?» находится в отчёте, а не остаётся загадкой. Это предупреждение, а не ошибка, потому что сами значения экспортировались полностью — отсутствует только удобство редактирования.
Карта gid
Используется только в режиме ExportUrl. Каждая запись сопоставляет имя вкладки значению #gid= таблицы (видно в URL браузера, когда выбрана вкладка). Инспектор настроек показывает карту, только когда она актуальна.
Вам не нужно копировать эти числа из браузера по одному. В инспекторе ассета настроек есть раздел Google Sheets, который заполняет карту за вас.
- В режиме ExportUrl Autofill gid from live читает список вкладок живой таблицы и полностью переписывает карту на его основе, а затем сохраняет ассет настроек.
- В режиме SheetsApi та же панель вместо этого предлагает Fetch live tab list, который просто показывает вам вкладки, которые сейчас есть в таблице. Этот режим обнаруживает gid самостоятельно и вообще не нуждается в карте.
Один нюанс: автозаполнение обращается к Sheets API, поэтому ему нужен настроенный ключ сервисного аккаунта, даже если сам импорт в режиме ExportUrl этого не требует. Без него оно останавливается и сообщает об этом, вместо того чтобы записать наполовину заполненную карту.
Хук проверки актуальности перед сборкой — устаревшее запекание проваливает сборку
Перед каждой сборкой pre-build хук проверяет три вещи для каждого закоммиченного сгенерированного типа Database:
- (i) запечённый SO существует;
- (ii) его отпечаток схемы совпадает с baseline;
- (iii) существует его регистрация в Addressables.
Любая неудача прерывает сборку с предложением конкретных действий (например, «откройте Tools/SheetForge/Data Studio, нажмите ↓ Pull from source, затем соберите проект»). Именно это делает безопасным то, что «запечённые SO исключены из Git»: клонированная машина или CI физически не могут отправить в сборку пустой кеш.
Похожие страницы
- Начало работы — настройка и безопасность ключа сервисного аккаунта
- Основные концепции — baseline и модель round-trip
- Data Studio — запись авторинга против отправки
- Возможности и ограничения — полный список ограничений Google/xlsx