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

PRO Players — топовые сборки

Секция админ-панели для публичных билдов от профессиональных игроков и стримеров. По сути это редакционно курируемая подборка «вот сборка топ-стримера X — поставь себе одной кнопкой».

Отличие от user_builds: pro_players — это админ-добавленные сборки знаковых игроков, не user-submitted. Админ сам собирает payload, заполняет про игрока, публикует.

Структура таблицы

create table pro_players (
  id              uuid primary key,
  player_name     text not null,        -- "Niko Hunter", "BulkinGotti"
  team            text,                 -- "Vortex", "Flame"
  avatar_url      text,
  cover_url       text,
  description     text,                 -- bio
  twitch_url      text,
  youtube_url     text,
  hnt_code        text unique,          -- ссылка на их билд
  build_payload   jsonb,                -- snapshot их установки
  display_order   int default 0,
  is_featured     bool default false,
  is_deleted      bool default false
);

Воркфлоу

Админ договаривается с игроком, тот говорит «у меня redux X v3, gunpack Y, armor Z». Админ:

  1. У себя на ПК ставит тот же билд.
  2. Открывает админ-панель → PRO Players → Add player.
  3. Вводит имя/команду, прикладывает аватар.
  4. Жмёт «Захватить мой текущий setup» — лаунчер делает CaptureHntPayloadAsync() (то же что для HNT-codes).
  5. Получает hnt_code — сохраняется в БД, привязан к pro_player row.

Capture-handler

HunterGraphics.Shell/Bridge/AppBridge.cs
public async Task<ProPlayerDto> AdminProPlayerCreateAsync(ProPlayerDraftDto draft)
{
    // Захват текущего payload админа
    var payload = await CaptureHntPayloadAsync();

    // Генерим публичный hnt_code (как для user_builds approve)
    var code = await _hntCodesRepo.GenerateUniqueCodeAsync();
    await _hntCodesRepo.InsertPublicAsync(code,
        JsonSerializer.SerializeToElement(payload, _hntJsonOpts));

    // Upload assets
    var avatarUrl = draft.AvatarBytes is null ? null
        : await _r2.UploadAsync($"pro-players/{draft.Id}/avatar.png", draft.AvatarBytes);
    var coverUrl  = draft.CoverBytes is null ? null
        : await _r2.UploadAsync($"pro-players/{draft.Id}/cover.png", draft.CoverBytes);

    // Insert
    return await _supa.InsertProPlayerAsync(new ProPlayerRow {
        Id = draft.Id, PlayerName = draft.PlayerName, Team = draft.Team,
        AvatarUrl = avatarUrl, CoverUrl = coverUrl,
        TwitchUrl = draft.TwitchUrl, YoutubeUrl = draft.YoutubeUrl,
        Description = draft.Description, HntCode = code,
        BuildPayload = JsonSerializer.SerializeToElement(payload, _hntJsonOpts),
        IsFeatured = draft.IsFeatured,
    });
}

UI юзера

Юзер в Browse → PRO Players видит грид аватарок. Клик → карточка игрока с описанием, twitch-link'ом, видео-демо, и большой кнопкой «Поставить билд». Кнопка вызывает HntCodeApplyAsync с привязанным hnt_code.

Это самый «магический» юзерский сценарий — один клик и у тебя точно такой же setup как у любимого стримера. Идея заимствована из MMO «купить сетап в один клик».

Update билда

Когда игрок меняет setup (новая ENB, обновлённый gunpack), админ перезахватывает:

public async Task<ProPlayerDto> AdminProPlayerRefreshBuildAsync(string proPlayerId)
{
    // Тот же flow как в Create, но обновляем existing row
    var payload = await CaptureHntPayloadAsync();
    var code = await _hntCodesRepo.GenerateUniqueCodeAsync();
    await _hntCodesRepo.InsertPublicAsync(code,
        JsonSerializer.SerializeToElement(payload, _hntJsonOpts));

    // Старый hnt_code остаётся валидным — юзеры с ним получат старый билд.
    // Это intentional чтобы те кто уже забукмаркал не "потеряли" билд.
    return await _supa.UpdateProPlayerBuildAsync(proPlayerId, code, payload);
}

is_featured = true ставит игрока в топ секции на главной. Обычно держим 5-8 featured.

Privacy и creds

Аватарки и описания берём от самих игроков с разрешением. Twitch/YouTube links без affiliate-кодов (мы не зарабатываем на cross-promotion'е). Если игрок просит убрать — soft-delete.

Дальше: Popularity →