Регистрация и составы
Заявка на турнир — это форма, окно, статус и допуск, а состав команды — отдельная сущность, которая живёт до балансировщика и драфта. Здесь описаны правила, по которым сервер принимает, отклоняет и достраивает эти данные; полный список маршрутов — в справочнике эндпоинтов.
Окно регистрации
Открытость самостоятельной регистрации определяет ровно одна строка расписания турнира — строка фазы REGISTRATION — плюс один флаг турнира. Текущая фаза турнира в решении не участвует: окно, дотянутое до начала LIVE, само по себе держит регистрацию открытой.
- Строки нет — закрыто. Это инверсия обычного правила фазовых окон (где отсутствие строки означает «всю фазу»), и она действует только для регистрации: свежесозданный турнир не должен принимать заявки до того, как организатор настроил форму.
COMPLETEDиARCHIVED— закрыто всегда, независимо от дат. Ошибка вends_atне может переоткрыть завершённый турнир.allow_late_registrationподнимает толькоends_at. Он не открывает турнир без строкиREGISTRATIONи не открывает окно, которое ещё не началось: «поздняя» регистрация — это всегда после конца, никогда до старта. Исходная дата закрытия при этом остаётся на странице турнира.
Чек-ин — другое окно и другое правило: статус турнира должен быть CHECK_IN и текущий момент должен попадать в строку CHECK_IN. Здесь действует обычный контракт фазовых окон — нет строки или ends_at равен null, значит окно занимает всю фазу.
Форма и заявка
Форма одна на турнир, у неё есть версии, и заявка всегда подаётся против текущей.
| Маршрут | Назначение |
|---|---|
GET /api/v1/tournaments/{tournament_id}/registration/form | Публичная форма и каталог саб-ролей; null, если формы нет |
POST /api/v1/tournaments/{tournament_id}/registration | Подать заявку за себя (201) |
GET /api/v1/tournaments/{tournament_id}/registration/me | Своя заявка или null |
PATCH /api/v1/tournaments/{tournament_id}/registration/me | Правка собственной заявки, пока она на рассмотрении |
DELETE /api/v1/tournaments/{tournament_id}/registration/me | Снять заявку |
POST /api/v1/tournaments/{tournament_id}/registration/me/check-in | Пройти чек-ин |
GET /api/v1/tournaments/{tournament_id}/registration/list | Публичный список участников |
Тело заявки — плоская карта ответов плюс версия формы, против которой они собраны. Устаревшую версию сервер отклоняет кодом form_version_stale (409): валидация всегда идёт против текущей схемы.
POST /api/v1/tournaments/42/registration HTTP/1.1
Host: owt.craazzzyyfoxx.me
Authorization: Bearer <токен сессии или API-ключ>
Content-Type: application/json
{
"form_version_id": 7,
"answers": {
"battle_tag": "Player#2100",
"roles": [
{ "role": "damage", "is_primary": true, "subrole": "hitscan" },
{ "role": "support", "is_primary": false }
],
"identity_discord": "player",
"stream_pov": true
}
}
Ключи встроенных вопросов фиксированы: battle_tag, smurf_tags, roles, stream_pov, reserve, public_notes, organizer_notes и identity_<provider> для discord, twitch, boosty, vk, youtube. BattleTag — отдельный встроенный вопрос со своей грамматикой, среди identity_* его нет. Порядок элементов в roles задаёт приоритет ролей; неизвестные и повторяющиеся коды отбрасываются.
Режим флекса у вопроса roles — optional (игрок сам называет роли), all_roles (играбельны все роли, приоритет игрок всё равно указывает) или forced (все роли играбельны и все основные). Форма, у которой не читается схема, ведёт себя как optional.
Остальное, что настраивает форма и что видно в ответе: auto_approve, show_ranks, hide_registrations (публичный список схлопывается в агрегат на сервере), max_participants (справочная вместимость, не лимит — заявка сверх неё проходит), max_substitutes (размер скамейки для командной регистрации), требование открытого профиля с областью main или all, подписочный переключатель со стадией и областью, и правила для команд капитанов: team_rank_min, team_rank_max, team_max_rank_spread, team_unique_identity, team_require_discord_guild.
Статусы
Статусов две независимые шкалы, и каждая — каталог воркспейса: встроенные значения можно переименовать и перекрасить, к ним можно добавить свои.
| Шкала | Встроенные значения |
|---|---|
registration | pending, approved, rejected, withdrawn, banned, insufficient_data |
balancer | not_in_balancer, excluded, incomplete, ready |
У пользовательских статусов шкалы balancer есть два флага: excludes_from_balancer (заявка не попадает в пул) и excludes_from_ready (заявка не считается готовой). У встроенных первый жёстко задан — истина только для not_in_balancer и excluded, — а второй всегда ложь.
ready и incomplete вычисляются из ролей и рангов и не записываются руками: попытка выставить их напрямую отвечает 400. Единственный путь — добавление одобренной заявки в пул, которое само ставит ready, если у каждой заявленной роли есть ранг, и incomplete иначе.
На одного игрока приходится одна заявка на турнир — это частичный уникальный индекс, а не проверка в коде. Снятие заявки после чек-ина запрещено самому игроку (409); с этого момента снять участника может только организатор. Снятая или отклонённая заявка терминальна: приглашение в команду её не воскрешает.
Допуск
Допуск — это два требования в фиксированном порядке, и порядок виден снаружи, потому что по нему рисуются шаги прогресса.
- Открытый профиль. Стадия жёстко зашита как
check_in: отдельной колонки под неё нет, пока организаторы её не попросят. Область —main(основной BattleTag) илиall(включая смурфов). - Подписка. Стадия берётся из формы (
registrationилиcheck_in); мусор иnullдаютcheck_in, потому что опечатка в настройке не должна начать отказывать всем подряд.
Стадии упорядочены: registration подразумевает и check_in. Само правило подписки живёт на воркспейсе, а не на турнире, — переключатель без правила ничего не требует, а не отказывает всем.
Состояния требования — satisfied, blocked, undetermined, not_applicable. Блокирует только blocked, то есть подтверждённый отказ; всё остальное, включая неизвестный провайдеру ответ, отваливается в undetermined и пропускает. Поэтому:
- отсутствие учётных данных Discord или Twitch даёт
unknownи пропускает — сбой провайдера не должен читаться как отменённая подписка; - при
mode=anyпричина сохраняется по каждому провайдеру отдельно; - на этапе подачи отказ смягчается, если у игрока ещё остаётся способ подтвердить подписку кодом;
- при области
teamдействующая отметка покрытия на команде закрывает требование за весь ростер и живой опрос провайдеров не выполняется.
Единственное правило, которое закрывается наглухо, — требование членства в Discord-гильдии для команд: проверка, которую невозможно выполнить, и воркспейс без привязанной гильдии дают блок, а не пропуск.
После чек-ина — пройденного самостоятельно или проставленного организатором — все требования считаются потраченными: заблокированные переезжают из blockers в overridden, а решение становится admitted или not_admitted только по признаку готовности заявки. Пересчитывать требования после чек-ина нельзя: именно так значок «не допущен» когда-то навсегда застревал на игроке, которого организатор впустил вручную.
Решение приходит одним из трёх значений — admitted, pending_check_in, not_admitted. Отказ на шлюзе — 400, по одной ошибке на каждый блокер (не только на первый) с машинным кодом вида open_profile_blocked или subscription_blocked.
Роли и ранги
Роль играбельна, когда она активна и у неё нашёлся ранг больше нуля. Ноль здесь — не «нет ранга»: загрузчик пула отбрасывает ранги <= 0, так что сохранённый ноль дал бы игрока, которого драфт может выбрать, а расчёт баланса молча теряет.
Ранг ищется по слоям, побеждает первый непустой:
- турнир: заявка → воркспейс → Overwatch;
- микс: автор → воркспейс → Overwatch.
Отсутствие строки — это и есть наследование, поэтому записи удаляют строку, а не обнуляют её: сохранённый 0 сломал бы проваливание на следующий слой.
Форма ростера
Коды слотов — tank, damage, support, flex. Встроенное значение — {"tank": 1, "damage": 2, "support": 2}.
- Размер команды — от 1 до 12 слотов. Ростер из одного слота разрешён.
draft_roundsравноteam_size - 1: капитан уже занимает слот. Для ростера из одного слота это ноль, и живой драфт для такого турнира просто не создаётся.- Разрешение: переопределение турнира → значение по умолчанию воркспейса → встроенные 5 на 5. Клиент получает уже разрешённую форму и сам её не пересчитывает — кроме предпросмотра неподтверждённой суммы в редакторе.
- Блокировки: нельзя менять умолчание воркспейса, пока его наследует турнир с идущим драфтом или формированием, и нельзя менять
team_formationили форму ростера, пока активна сессия драфта.
Слот flex — это слот, а не роль: у него нет ранга, и роль в него не записывается. Если каждый слот формы — flex, ролевые счётчики, фильтры и ролевая валидация выключаются целиком.
Команды капитанов
Команда, собранная капитаном на регистрации, — отдельная сущность, не тот ростер, который создают балансировщик и драфт. Капитан основывает команду и регистрируется в ней одним вызовом: та же форма и тот же допуск, что у одиночного участника, а разделение на «создать команду» и «зарегистрировать капитана» оставило бы окно, в котором команда существует без капитана.
POST /api/v1/registration-teams/invites/accept HTTP/1.1
Host: owt.craazzzyyfoxx.me
Authorization: Bearer <токен сессии>
Content-Type: application/json
{ "token": "<сырой токен из ссылки>" }
Приглашения. Два адреса на одной сущности. С target_registration_id это внутреннее предложение конкретному свободному агенту — токена у него нет вовсе, получатель видит его в собственных приглашениях. Без него выпускается ссылка: сырой токен возвращается ровно один раз в ответе на создание, хранится только его sha256, и сравнение идёт в постоянном времени. Срок жизни задаётся ttl_days (по умолчанию 7, допустимо от 1 до 90). Накопительный потолок — 60 приглашений на команду с учётом отозванных и отклонённых; сброс потолка делает организатор, и он сдвигает отсечку по времени, а не удаляет историю.
Приглашение — не заготовка заявки: оно не должно раздувать публичный счётчик участников. Ожидающее приглашение резервирует слот, который ещё можно предложить, но приёму не мешает: приём смотрит на открытые слоты, а не на непредложенные. Само погашение — один сторожевой UPDATE с возвратом строки, поэтому две одновременные попытки открыть одну ссылку не могут выиграть обе; проигравший получает 409. Ограничитель частоты на приёме отваливается в разрешение — настоящая защита здесь энтропия токена.
Игрок, у которого уже есть одиночная заявка, присоединяется к команде существующей строкой: присланное тело заявки при этом игнорируется, его собственные ответы остаются. Предпросмотр приглашения — единственная анонимная поверхность в этом наборе: он разворачивает токен ссылки в турнир, команду, слот, признак запасного, срок и признак «ещё можно принять», не погашая его и не раскрывая ростер. Токен, не совпавший ни с чем, отвечает 404, а протухшее или отозванное приглашение — своим состоянием, а не ошибкой.
Занятость ростера. is_complete считается только по стартовым слотам; запасные живут в отдельном бюджете max_substitutes и слота в форме ростера не занимают. Статус команды — forming, complete, rejected или disbanded; поверх него есть ортогональная ось допуска организатора — pending, accepted, waitlisted. Капитан может заморозить укомплектованный ростер, после чего правки отвечают roster_locked до разблокировки организатором.
Проверки состава считаются только по стартовым: минимальный и максимальный ранг и максимальный разброс (без ранга заявка не проходит минимум и максимум, но в разбросе не участвует), уникальность идентичностей относительно остальных команд турнира и членство в Discord-гильдии.
Экспорт в турнирные команды пропускает команды из списка ожидания, неукомплектованные, пустые и непокрытые подпиской, и каждая пропущенная возвращается с кодом — team_waitlisted, team_incomplete, team_empty, team_subscription_uncovered. Молчаливой потери команды нет. Экспорт здесь разрушающий, а не диффовый, поэтому он отказывается выполняться, если таблица уже посчитана по командам, которые он заменять не будет.
См. также
- Балансировщик, драфт и миксы — что происходит с пулом после регистрации.
- Турниры, стадии и сетка — фазы и расписание, которыми управляются окна.
- Воркспейсы и права — где живут каталоги статусов и правило подписки.
- Справочник эндпоинтов — полный список маршрутов.