Files
mixmaker/memory_bank/domain_model.md
lemintare 966c81ba12
Some checks failed
CI / backend (push) Has been cancelled
CI / frontend (push) Has been cancelled
CI / compose (push) Has been cancelled
Add RSVP role synchronization and localization support for Discord
This commit introduces functionality for managing event-specific RSVP roles in Discord, including roles for "Registered", "Going", "Maybe", and "Not Going". The `RoleWorker` has been enhanced to handle these roles based on user registrations, with localization support for role names in Russian and English. Additionally, the database schema has been updated to accommodate new role types and actions, and the service layer has been modified to trigger role synchronization upon relevant event updates. Tests have been added to ensure the correct behavior of the new RSVP handling logic.
2026-07-19 11:42:40 +03:00

15 KiB
Raw Blame History

Предметная модель

Основные сущности

Player

Игрок связан с Discord-аккаунтом и имеет отображаемое имя и собственный Overwatch-ранг для каждой роли: Tank, Damage, Support. Лестница содержит 40 значений от Bronze 5 до Champion 1: Bronze, Silver, Gold, Platinum, Diamond, Master, Grandmaster, Champion, каждый с дивизионами 5 → 1. Для вычислений значения преобразуются в ordinal 140.

Игрок редактирует ранги самостоятельно; Admin может видеть время последнего изменения. Игрок выбирает любое число предпочитаемых ролей, до трёх желаемых союзников и до трёх игроков для avoid. Предпочтения и avoid являются мягкими пожеланиями, а не гарантией состава; один игрок не может одновременно находиться в обоих списках.

Account

Учётная запись Discord с глобальной ролью Player, Moderator или Admin. Moderator имеет те же операционные права, что Admin, но не может назначать и снимать модераторов. Moderator назначается только Admin; начальные Admin задаются списком Discord ID в конфигурации развёртывания. Изменение роли записывается в аудит.

Event

Запланированный игровой вечер: название, дата и время, часовой пояс, описание, дедлайн регистрации, формат команд, регламент, турнир и текущее состояние.

EventRegistration

Отметка игрока в календаре со статусом Going, Maybe или NotGoing. Игрок меняет собственный статус, а Admin может изменить его за игрока. Регистрация хранит автора и время последнего изменения, чтобы интерфейс явно показывал административное действие. В момент закрытия регистрации Admin подтверждает список участников; только Going-игроки по умолчанию попадают в балансировку.

Discord-анонс события

Созданный микс проецируется во внешний Discord-канал как информационный анонс со ссылкой на страницу Event. Параметр команды создания определяет, сопровождается ли анонс уведомлением @everyone; он не является сохраняемым состоянием Event. Анонс не является источником состояния регистрации: RSVP, доступность события и все переходы workflow по-прежнему определяет backend Mixmaker. Недоступность Discord не отменяет валидное создание Event.

Discord-роли состава

Подтверждённый Roster является источником desired state для внешних Discord-ролей. Каждый участник получает временную роль с Team.Name, роль своего slot-а Tank/Damage/Support, а капитан — отдельную ${Team.Name} Captain. Discord role ID и выданные ботом назначения сохраняются отдельно от доменных Team и Account, чтобы повторная синхронизация была идемпотентной.

Team/captain roles принадлежат конкретному Event и удаляются при отмене, завершении, удалении или возврате к редактированию roster. Tank/Damage/Support общие для guild: после cleanup их участники пересчитываются по всем оставшимся активным событиям. Guest и пользователь, отсутствующий в guild, не блокируют подтверждение состава и фиксируются как предупреждение интеграции.

Для каждого Event бот также поддерживает четыре временные RSVP-роли: общую Зарегистрирован: {Event.Name} для любого явного ответа и взаимоисключающие Идёт, Возможно, Не идёт. Изменение EventRegistration атомарно меняет desired status-role; удаление регистрации снимает все RSVP-назначения. Эти роли сохраняются после закрытия регистрации и удаляются вместе с другими event-specific ролями при завершении, отмене или удалении Event.

Жизненный цикл события

Событие проходит серверно контролируемые состояния:

  1. RegistrationOpen — игроки меняют RSVP, Admin добавляет гостей;
  2. RegistrationClosed — список участников зафиксирован, обычные RSVP заблокированы;
  3. Balancing — создаются варианты команд и резерв;
  4. RostersDraft — Admin выбирает вариант, меняет игроков одной роли местами и заменяет их резервными;
  5. RostersConfirmed — составы 1/2/2 и капитаны проверены и заблокированы;
  6. BracketDraft — staff задаёт порядок Bo3 и источники Team / Winner / Loser для слотов;
  7. Live — созданы готовые серии, разрешены только действия текущего шага жеребьёвки, банов или результата;
  8. Completed — выполнены все запланированные матчи;
  9. Cancelled — Admin отменил событие; оно остаётся в календаре, но игровые действия заблокированы.

Переходы выполняются отдельными командами API с проверкой ожидаемой версии. До запуска скрима staff-пользователь может вернуться на один этап назад: черновик удаляется при возврате из RostersDraft, а состав снова разблокируется при возврате из RostersConfirmed. Возврат из Live и Completed запрещён. Нельзя начать скрим без минимум двух полных команд, валидного состава 1/2/2 и капитана в каждой команде. После RostersConfirmed обычная перестановка запрещена. Аварийная замена во время Live доступна staff-пользователю, должна сохранять ролевой состав и записывается в аудит; если заменён капитан, назначение переходит вошедшему вместо него игроку. Полное удаление события является отдельной staff-командой и каскадно удаляет его RSVP, составы, roster draft, серии, турнир и event-scoped аудит.

Team

Состав игроков, назначенный staff-пользователем капитан, цвет/название и рассчитанные показатели силы. Капитан должен входить в текущий состав команды; переназначение фиксируется в аудите. После запуска скрима капитан может задать своей команде отображаемое название; staff может переименовать любую команду, изменение синхронизируется с live- и spectator-экранами.

  • общий средний рейтинг;
  • средний рейтинг по каждой роли;
  • распределение игроков по ролям;
  • оценка доверия к балансу.

Ruleset

Версионируемый набор правил серии:

  • формат Bo3;
  • последовательность режимов;
  • пулы карт по режимам;
  • порядок банов карт и героев;
  • лимиты банов по ролям;
  • ограничения повторных банов в серии.

Series

Матч двух команд в рамках события или турнирной сетки. Хранит жеребьёвку, порядок первого бана, карты, счёт, статус и итогового победителя.

MapResult

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

MapDraft

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

HeroDraft

Четыре бана перед картой в порядке A → B → A → B. Каждая команда выбирает двух героев разных ролей. Героя, которого команда уже банила в этой серии, она не может забанить снова; героя, ранее забаненного соперником, банить можно.

Tournament

Versioned граф матчей, в котором каждый слот ссылается на конкретную Team либо Winner/Loser матча из предыдущей колонки. Готовые зависимости материализуются в отдельные Bo3-серии автоматически; несколько готовых матчей могут идти параллельно. Последняя колонка содержит один матч, победитель которого становится общим победителем. Для трёх команд стартовый шаблон проводит AB, затем Loser(M1)C, затем финал победителей первых двух матчей.

Правила текущего регламента

Карты

Базовая последовательность Bo3:

  1. Control;
  2. Hybrid или Escort;
  3. Control-тайбрейкер, если счёт 1:1.

Альтернативная последовательность: Control → Push → Escort.

Первый бан первой карты определяется жеребьёвкой. Затем команды по очереди исключают карты внутри пула режима, пока не останется одна. После завершения карты FSM переходит в MapPick: проигравшая команда выбирает следующую доступную карту, после чего начинается hero draft.

Герои

  • перед каждой картой банятся четыре героя;
  • порядок: A → B → A → B;
  • каждая команда банит двух героев разных ролей;
  • один и тот же герой может быть забанен командой только один раз за серию;
  • бан героя соперником не расходует собственную возможность забанить этого героя позже.

Балансировка

Целевая функция должна учитывать не только близость общего среднего рейтинга команд, но и:

  • разницу средних рейтингов по ролям;
  • корректность состава 1 Tank / 2 Damage / 2 Support;
  • назначение игроков на предпочитаемые роли;
  • разведение по разным командам игроков из avoid-списков;
  • попадание выбранных игроком союзников в одну команду;
  • запрет повторения составов при регулярных играх;
  • заранее заданные пары или ограничения «вместе/раздельно».

Алгоритм должен возвращать не только составы, но и понятную оценку качества и причины оставшегося дисбаланса.

Жёсткие ограничения 1/2/2 и уникальности игроков всегда важнее предпочтений. Разница рейтингов имеет больший вес, чем пожелания. В MVP используется детерминированная эвристика: несколько начальных назначений, взвешенная целевая функция и локальные обмены игроков одной роли. Архитектура допускает замену на CP-SAT/MILP solver.

Ручная корректировка не изменяет исходные варианты балансировщика: выбранный вариант копируется в редактируемый roster draft. Обмен допускается только между слотами одинаковой роли. Замена из резерва назначает игрока в конкретный ролевой слот и пересчитывает показатели обеих команд и резерва.

Доменные события

  • EventCreated;
  • PlayerJoinedEvent;
  • EventRegistrationChanged;
  • TeamsBalanced;
  • TeamCaptainAssigned;
  • CoinTossResolved;
  • MapBanned;
  • MapSelected;
  • HeroBanned;
  • MapResultRecorded;
  • MapResultCorrected;
  • SeriesCompleted;
  • BracketAdvanced.