Перейти к содержанию

Жизнь канала

Отдельный режим в Владелец → Интерактивные игры → Жизнь канала. У канала появляется случайный персонаж: от рождения до старости он проживает события, а зрители выбирают его дорогу. Режим не использует валюту и механику PulseCaster City.

Первая версия содержит 6 игровых стран, 34 сцены выбора с несколькими ответами и 107 бытовых эпизодов. Страна задаёт имена и базовый уровень игровой экономики; это авторская модель, а не симуляция населения, законодательства и традиций мира. События и тексты оригинальные. Постоянной генерации через AI нет.

Начало и продолжение

  1. Включите режим в настройках. Для проверки вне эфира нажмите Репетиция.
  2. Настройте год (5–300 секунд, по умолчанию 45), выбор (15–180 секунд, по умолчанию 45) и влияние ведущего (0–30%, по умолчанию 15%). Эти правила сохраняются при рождении и действуют до завершения жизни.
  3. Нажмите Начать жизнь на эфире либо включите автозапуск с задержкой 0–240 минут от начала эфира. Его можно отложить на 10 минут или пропустить только для текущего эфира.
  4. Добавьте публичную ссылку и источники OBS из вкладки OBS и ссылки.

На канале одновременно существует одна активная история. Один эфир связывается только с одной жизнью: переподключение и перезапуск сервиса не создают другого персонажа. Завершённая история остаётся в альбоме; следующую эфирную жизнь можно начать на следующем эфире. Репетиции не занимают эфир, но также требуют завершить текущую историю перед новой.

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

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

Решения чата

Зритель открывает /life/ЛОГИН_КАНАЛА, входит через Twitch и выбирает вариант. До окончания времени бесплатный голос можно изменить. При включённых командах работает !жизнь КОД НОМЕР или !life КОД НОМЕР; код показан у текущего решения. Команды обрабатываются без ответов бота. Голоса с сайта и чата связываются по Twitch ID, поэтому не складываются в два бесплатных голоса.

При участии ведущего его выбор получает заданный процент, остальное влияние распределяется между голосами зрителей. Например, при 15% влияния ведущего чат распределяет 85%. Без голоса ведущего чат распределяет все 100%. Ведущий не может один запустить автоматическое решение: если зрителей нет, выбор остаётся ждать участников. При равенстве используется воспроизводимый жребий.

Предложить смену направления открывает возрастное голосование о дальнейшем образе жизни. Оно доступно с 6 лет между текущими выборами. Прямое решение ведущего доступно только при включённом Разрешить прямой выбор ведущего; в летописи оно явно подписывается.

Последствия связаны с прежними решениями: учёба и квалификация влияют на доход, занятия и покупки открывают события, поддержка близких возвращается в воспоминаниях. Рискованный ответ не гарантирует успеха. Сцены с ребёнком учитывают его собственный возраст, а события с близкими — жив ли персонаж.

Траты и баллы Twitch

Во вкладке Траты ведущий покупает книги, инструмент, обучение, поездки, жильё и другие возможности за деньги персонажа. Возраст, достаток, повторные покупки и пауза выбора ограничивают доступность. Во время открытого выбора покупки закрыты, чтобы доступные ответы не менялись посреди голосования. Деньги условные и не связаны с донатами или выплатами.

Усиление за баллы канала необязательно. Включите его, задайте цену и нажмите Синхронизировать награду Twitch. Требуется токен владельца с channel:manage:redemptions; бесплатные голосования этого права не требуют. Награда создаётся приложением PulseCaster с очередью запросов. После изменения цены или выключателя синхронизируйте её снова.

В поле награды зритель вводит КОД НОМЕР, скопированные со страницы. Покупка даёт один дополнительный голос в конкретный вариант, один раз на зрителя для каждого выбора. Обычный голос можно менять, оплаченный вариант фиксируется. Если это первое участие зрителя, одновременно появляется его обычный голос. Ведущий участвует только своим взвешенным голосом; его покупка возвращается. Выключение усилений прекращает приём сразу; включение для жизни, родившейся без усилений, применяется к следующему персонажу.

Устаревший код, опоздание, повторное усиление, пауза и выключенный режим приводят к возврату баллов. Решение о принятии и возврате сначала сохраняется в БД, затем передаётся Twitch; повторная доставка события не добавляет второй голос. Неудавшиеся подтверждения повторяются раз в 30 секунд. После сбоя между Twitch и записью результата проверяется фактический статус покупки. Ручная отмена модератором уже принятой покупки не пересчитывает завершённую сцену; расхождение отмечается в журнале сервиса.

OBS, публичная страница и альбом

  • Компактная панель — портрет, возраст, страна, показатели и деньги. Начните с размера Browser Source 454×340.
  • Попапы и журнал — рождение с постепенным раскрытием, текущий выбор, новые события и финал. Начните с 454×900; при отсутствии событий фон прозрачный. Открытый выбор имеет приоритет над бытовыми событиями.
  • Публичная страница — полная история и голосования; адаптируется в узкую колонку и подходит для отдельного источника браузера. В отличие от двух OBS-представлений её фон непрозрачный.

Источники используют публичные URL без секретов управления. Управлять жизнью можно только с правами кабинета канала. Зрителю для чтения авторизация не нужна; для голоса нужна его Twitch-сессия. Публичный ответ не содержит seed, будущие ветки, внутренние флаги и личность голосующих.

Альбом хранит последние 50 историй канала: портрет, возраст, занятие и переход к полной летописи. У каждой истории постоянный адрес /life/ЛОГИН/UUID. Завершённая жизнь получает итоговую биографию. История, которую ведущий остановил, отмечается незавершённой и не попадает в сравнение завершённых жизней.

Во вкладке Другие истории показываются до 30 завершённых эфирных историй всех каналов: недавние, долгие жизни или счастье в финале. Репетиции не участвуют. Количество участников — уникальные Twitch ID зрителей, голосовавших хотя бы раз; это не оценка всех зрителей эфира. Финальные деньги не используются как общий рейтинг стран.

API и эксплуатация

Пульт: GET /api/v1/channels/{id}/games/life. Настройки: PUT .../settings. Действия POST .../{start,pause,resume,finish,focus,purchase,host-vote,resolve,override,skip-autostart,defer-start,sync-reward} защищены правами раздела интерактивных игр.

Публичные адреса: GET /api/v1/public/life/{login}/snapshot?session=UUID, POST .../vote, GET .../overlay?view=compact|events, GET /api/v1/public/life/all/comparisons?sort=recent|age|happiness. Голос передаёт session_id, decision_id, option; входной Twitch ID не принимается. JSON ограничен 8 KiB, неизвестные поля запрещены. Публичное голосование проверяет origin и ограничивает частоту смены голоса. Публичный снимок общий для зрителей и кэшируется сервером на одну секунду. Read-only режим отключает часы, управляющие действия и приём голосов.

Часы запускаются вместе с public API (включая fallback основного процесса), проверка каждые 2 секунды. Изменения сериализуются транзакционной блокировкой PostgreSQL по каналу. Twitch decision runtime принимает команды и покупки и повторяет незавершённые расчёты с Twitch. Для выпуска нужна миграция 20261024112227_add_channel_life_game.sql; порядок описан в базе данных.

Проверка на изолированной тестовой БД: go test ./src/server -run TestLifeStandaloneRuntimesSharePersistedHistory -count=1 -v при заданном CITY_TEST_POSTGRES_DSN. Тест создаёт и удаляет отдельную БД, применяет штатные Goose-миграции и проверяет пульт, публичный API и восстановление состояния. Нужен тестовый PostgreSQL с правом создавать базы.