Подключите свою игру Roblox к Baseplate
Baseplate — это один открытый серверный модуль Luau со встроенным клиентским скриптом. Никаких внешних зависимостей, подгрузки ассетов и клиентских токенов.
Быстрый старт (2 минуты)
- Создайте игру в разделе Добавить игру и скопируйте токен
bpt_.... - Установите плагин Baseplate Connect в Studio и вставьте одноразовый код или скачайте Baseplate.rbxmx и вставьте его в
ServerScriptService. - Включите Game Settings → Security → Allow HTTP Requests.
- Добавьте серверный
ScriptвServerScriptService, который вызываетBaseplate.start. - Нажмите Play в Studio. Точка подключения в мастере настройки загорится зелёным через 15 секунд.
1. Добавьте модуль в вашу игру
Вариант А — плагин Baseplate Connect (в один шаг): скачайте BaseplateConnect.rbxmx в папку плагинов Roblox Studio, нажмите Baseplate Connect на вкладке Plugins и вставьте код со страницы настройки игры. Плагин сам создаст модуль Baseplate, скрипт BaseplateLoader в ServerScriptService, включит HttpService.HttpEnabled и отправит 3D-слепок карты уровня.
Вариант Б — вручную через .rbxmx: скачайте Baseplate.rbxmx, в Roblox Studio нажмите правой кнопкой по ServerScriptService → Insert from File… и выберите скачанный файл. Появится ModuleScript с именем Baseplate (со вложенным клиентским скриптом Client).
Оставьте Client внутри Baseplate — при вызове Baseplate.start() модуль автоматически копирует его в StarterPlayerScripts, чтобы измерять FPS клиента, устройство, время бездействия (AFK), меню Escape и открытые окна ScreenGui.
2. Включите HTTP-запросы
В Roblox Studio откройте File → Game Settings → Security и включите Allow HTTP Requests. Если игра ещё не опубликована в Roblox, сначала сохраните её в Roblox или выполните в Command Bar: game:GetService("HttpService").HttpEnabled = true.
Рекомендуется для продакшена: храните токен в Creator Hub → Creations → [Ваша игра] → Settings → Secrets с именем baseplate_token и читайте через HttpService:GetSecret("baseplate_token"), чтобы токен не лежал в открытом тексте в файле места.
3. Запустите Baseplate
Создайте Script в ServerScriptService:
local HttpService = game:GetService("HttpService")
local Baseplate = require(game.ServerScriptService.Baseplate)
Baseplate.start({
token = HttpService:GetSecret("baseplate_token"),
studio = true, -- send data from Studio playtests too (kept separate from live)
})Или при быстром тесте в Studio передайте токен строкой:
local Baseplate = require(game.ServerScriptService.Baseplate)
Baseplate.start({
token = "bpt_your_token_here",
studio = true,
})Все опции Baseplate.start(options)
| Опция | Тип | По умолчанию | Описание |
|---|---|---|---|
token | string | Secret | обязательно | Токен игры bpt_... или HttpService:GetSecret("baseplate_token"). |
studio | boolean | false | При false SDK не запускается в Roblox Studio. Установите true, чтобы проверять события при плейтестах. |
debug | boolean | false | Печатает статус очереди и отправку каждой пачки в окно Output. |
client | boolean | true | Устанавливает лёгкий клиентский скрипт для сбора FPS, устройства, AFK, меню Escape и открытых окон GUI. |
deaths | "auto" | "manual" | "auto" | Автоматически слушает Humanoid.Died. Укажите "manual", если вызываете Baseplate.death(player, cause) сами. |
firstJoinStore | boolean | true (live) | Записывает 1 байт в DataStore BaseplateSDK_FirstJoin_v1 при первом входе игрока, чтобы точно отличать новичков от вернувшихся. |
Плейтесты в Studio и живые данные
Сессии, записанные в Roblox Studio (studio = true), помечаются на сервере как тестовые. Они скрыты на всех графиках по умолчанию, поэтому ваши плейтесты никогда не испортят статистику удержания, выручки или причин ухода. Чтобы проверить их во время отладки, нажмите переключатель Studio в шапке кабинета.
Что собирается автоматически
Сразу после вызова Baseplate.start — без единой дополнительной строчки кода — вы получаете:
Справочник API (все вызовы необязательны)
Вызывайте эти функции из любого серверного Script после Baseplate.start. Каждый метод защищён внутренним pcall и никогда не вызовет ошибку в вашем коде.
Baseplate.event(player, name, fields?)
Записывает произвольное событие (синоним: Baseplate.track). Поля fields — таблица до 16 ключей со строками, числами или boolean. Также добавляется в список 15 последних действий игрока.
Baseplate.event(player, "QuestComplete", {
quest = "FindTheKey",
seconds = 42,
hard_mode = true,
})
-- Server-wide event (no player):
Baseplate.event(nil, "BossSpawned", { boss = "Dragon", level = 5 })Baseplate.step(player, stepName)
Отмечает прохождение шага воронки. В разделе Почему уходят → Воронки вы можете собрать воронку из любых шагов и увидеть, где именно отваливаются игроки.
Baseplate.step(player, "1_Spawned")
Baseplate.step(player, "2_OpenedShop")
Baseplate.step(player, "3_BoughtStarterSword")
Baseplate.step(player, "4_EnteredFirstDungeon")Baseplate.tutorial(player, stepNumber)
Сообщает Baseplate, на каком шаге обучения находится игрок (1, 2, 3…). Когда обучение пройдено, вызовите Baseplate.tutorial(player, 0). Если игрок выйдет при stepNumber > 0, сессия получит причину ухода Вышел в обучении (quit_tutorial).
Baseplate.tutorial(player, 1) -- started step 1
Baseplate.tutorial(player, 2) -- reached step 2
Baseplate.tutorial(player, 0) -- finished!Baseplate.currency(player, currency, amount, source?)
Записывает начисление (amount > 0) или трату (amount < 0) внутриигровой валюты. Строит графики Экономики (источники, стоки, инфляция и детектор игроков, застрявших без заработка 5+ минут).
Baseplate.currency(player, "Coins", 100, "QuestReward")
Baseplate.currency(player, "Coins", -50, "BuySword")
Baseplate.currency(player, "Gems", 5, "DailyBonus")Baseplate.receipt(receiptInfo)
Покупки Game Pass отслеживаются автоматически. Для Developer Products добавьте одну строку внутри MarketplaceService.ProcessReceipt — дубликаты отсекаются по PurchaseId:
local MarketplaceService = game:GetService("MarketplaceService")
MarketplaceService.ProcessReceipt = function(receiptInfo)
-- your code that grants the item to the player...
Baseplate.receipt(receiptInfo)
return Enum.ProductPurchaseDecision.PurchaseGranted
endBaseplate.variant(player, testName, variants) → string
Детерминированно назначает игроку вариант A/B-теста по хешу UserId + testName (один и тот же игрок всегда получает тот же вариант на любом сервере без чтения DataStore) и сравнивает группы на вкладке A/B-тесты.
-- 50 / 50 split:
local group = Baseplate.variant(player, "starter_pack", { "control", "cheaper" })
if group == "cheaper" then
-- show the 49 Robux price
else
-- show the 99 Robux price
end
-- Custom weights (80% / 20%):
local layout = Baseplate.variant(player, "shop_layout", { old = 80, new = 20 })Baseplate.loaded(player)
По умолчанию игрок считается загрузившимся при первом спавне персонажа. Если у вас свой загрузочный экран или чтение профиля DataStore, вызовите Baseplate.loaded(player) после готовности — тогда ранние выходы будут точно помечены как Вышел во время загрузки.
-- After DataStore profile is ready and character is spawned:
Baseplate.loaded(player)Baseplate.mark(player, label)
Добавляет короткую метку в список 15 последних действий игрока перед выходом и ставит точку на тепловой карте.
Baseplate.mark(player, "OpenedShop")
Baseplate.mark(player, "StartedBossFight")Baseplate.death(player, cause?)
Записывает смерть игрока с указанием причины (например, "Lava" или "BossSlam") и текущими 3D-координатами на карте.
Baseplate.start({
token = HttpService:GetSecret("baseplate_token"),
deaths = "manual",
})
-- Inside your combat / health system:
Baseplate.death(player, "Lava")
Baseplate.death(player, "SkeletonKing")Baseplate.kick(player, reason?)
Помечает сессию как кикнутую сервером и вызывает player:Kick(reason), чтобы причина ухода записалась как Кикнут сервером, а не обычный выход.
Baseplate.kick(player, "AFK timeout")Baseplate.status() → table
Возвращает текущее состояние очереди и HTTP-отправки для диагностики в Studio.
print(Baseplate.status())
-- { started = true, queued = 3, dropped = 0, sent = 4, lastStatus = 202 }Как работают причины ухода (14 правил)
При выходе игрока Baseplate проверяет сигналы сессии сверху вниз — первое сработавшее правило определяет причину ухода:
| # | Причина | Когда назначается |
|---|---|---|
| 1 | server_shutdown | Сервер закрылся штатно (обновление или перезапуск Roblox). |
| 2 | kicked | Вызван Baseplate.kick(player, reason). |
| 3 | left_loading | Игрок вышел до первого спавна персонажа или до вызова Baseplate.loaded(player). |
| 4 | bounced | Сессия длилась менее 60 секунд. |
| 5 | declined_purchase | Игрок закрыл окно покупки Robux без покупки за ≤ 120 с до выхода. |
| 6 | quit_tutorial | Игрок вышел, пока Baseplate.tutorial(player, step) был больше 0. |
| 7 | afk | Не было ввода с клавиатуры/мыши/тача 3+ минуты перед выходом. |
| 8 | poor_performance | Средний FPS клиента ниже 20 или пинг выше 450 мс. |
| 9 | script_errors | За время сессии произошло 3 или более ошибок скриптов. |
| 10 | roblox_menu | Игрок открыл системное меню Escape и нажал Leave. |
| 11 | gui_open | Игрок вышел при открытом внутриигровом окне ScreenGui (например, Shop). |
| 12 | long_session | Успешная сессия длительностью 30+ минут без негативных сигналов выше. |
| 13 | normal | Обычная сессия от 1 до 30 минут без проблем. |
| 14 | server_lost | Сервер перестал присылать пульс без штатного BindToClose (краш сервера). |
Ключи и доступ команды
- Привязка к Universe ID: при первой живой пачке из Roblox игра автоматически привязывается к вашему
game.GameId, поэтому чужой плейс не сможет слать события по вашему токену. - Перевыпуск токенов: в разделе Настройки → Ключи можно создать новый токен и отозвать старый в один клик (в БД хранится только SHA-256 хеш).
- Команда: приглашайте коллег по одноразовой ссылке с ролью Member или Viewer (только чтение).
Оповещения в Discord и Telegram
В разделе Настройки → Оповещения подключите вебхук Discord или Telegram-бота. Проверка выполняется каждые 5 минут с паузой 2 часа между повторными срабатываниями:
| Оповещение | Когда срабатывает |
|---|---|
| Падение онлайна | Текущий онлайн упал более чем на 50% от среднего за прошлый час или от этого же часа вчера. |
| Всплеск ошибок | 20+ ошибок за 15 минут при росте ≥ 3× к суточной норме, либо новая ошибка в свежей версии. |
| Падение серверов | 3 или более живых серверов замолчали за последние 15 минут без закрытия. |
| Всплеск причины ухода | Доля негативной причины ухода за 2 часа выросла ≥ 2× к норме за 7 дней. |
| Новая версия и отчёт 24 ч | При выходе нового PlaceVersion и автоматический отчёт сравнения через 24 часа. |
| Ежедневная сводка | Раз в сутки в выбранный час UTC: игроки, новые, пик CCU, средняя сессия, Robux и топ причин ухода. |
Влияние обновлений
Каждая сессия и событие помечаются номером game.PlaceVersion. На вкладке Обновления Baseplate автоматически сравнивает каждую версию с предыдущей по 8 метрикам на игрока (время в игре, длина сессии, доля отскоков, ошибки на 100 сессий, FPS, конверсия, Robux на игрока и удержание D1).
«Почему изменилось»
Нажмите на любую плитку метрики на Обзоре, чтобы открыть разбор Почему изменилось — автоматическое сравнение текущего периода с предыдущим по составу платформ, стран, источников входа, доле новичков, причинам ухода, версиям плейса, ошибкам и среднему чеку.
Заметки на графиках и выгрузка CSV
Добавляйте заметки (релиз, промокод, реклама, событие) прямо под графиком Обзора, чтобы видеть показатели за 24 часа до и после отметки. Каждая таблица и график поддерживают выгрузку в CSV.
Данные и приватность
- Что мы храним: в рамках вашей игры сохраняются UserId, ник и DisplayName (зашифрованные AES-256-GCM в БД), страна, устройство, FPS/пинг, длительность сессии, причина ухода, покупки и ваши кастомные события.
- Чего мы никогда не собираем: чат, личные сообщения, голос, пароли, IP-адреса игроков, куки
.ROBLOSECURITYи любые данные вне вашей игры. Мы никогда не связываем профили игроков между разными играми. - Право на удаление (Right to Erasure): в разделе Настройки → Приватность введите любой Roblox
UserIdили подключите вебхук Roblox Right to Erasure для мгновенного удаления всех данных игрока.
Сроки хранения данных
| Данные | Free | Pro |
|---|---|---|
| Сырые события и тепловые точки | 7 дней | 30 дней |
| Индивидуальные сессии | 30 дней | 90 дней |
| Дневные итоги, когорты и сводки игроков | Пока существует игра | Пока существует игра |
Лимиты и бюджет HTTP
- Бюджет HttpService: не более 6 HTTP-запросов в минуту с одного сервера Roblox (~1,2% от лимита Roblox в 500 запросов/мин): 4 лёгких пинга присутствия + 1–2 пачки событий.
- Размер пачки: до 500 событий или 240 КБ за запрос; кольцевая очередь в памяти хранит до 6 000 строк.
- Кастомные поля: до 16 полей в событии; ключи —
snake_caseдо 32 символов; строковые значения — до 200 символов.
Решение частых проблем
| Симптом | Решение |
|---|---|
[Baseplate] not started: studio is off | Вы запустили тест в Studio с настройками по умолчанию. Передайте studio = true в Baseplate.start. |
HttpService is not allowed to access ROBLOX resources / HttpEnabled | Включите File → Game Settings → Security → Allow HTTP Requests в Roblox Studio. |
lastStatus = 401 | Токен неверный или отозван. Создайте новый токен в разделе Настройки → Ключи. |
lastStatus = 403 (universe_mismatch) | Токен уже привязан к другому Universe ID. Сбросьте привязку на странице настройки или создайте отдельную игру в кабинете. |
| Индикатор зелёный, но графики Обзора показывают 0 | Сессии из Studio скрыты по умолчанию. Включите кнопку Studio в правой части верхней шапки кабинета. |
ИИ и CLI (MCP)
Baseplate включает официальный CLI и MCP-сервер (24 инструмента только для чтения), чтобы вы могли спрашивать Claude Desktop, Claude Code, Cursor, ChatGPT или терминал о своей игре и публичных чартах Roblox. Откройте ИИ и CLI, чтобы выпустить токен bpt_agent_....
npx baseplate login
npx baseplate games
npx baseplate live <gameId>
npx baseplate overview <gameId> --range 7dHTTP-приём (для своих клиентов)
Модуль Luau отправляет сжатые JSON-пачки на POST /v1/ingest с заголовком Authorization: Bearer bpt_... и лёгкие пинги присутствия на POST /v1/presence каждые 15 секунд:
{
"v": 1,
"sdk": "0.10.3",
"game": 1234567890,
"place": 9876543210,
"job": "server-job-uuid",
"ver": 42,
"seq": 1,
"dropped": 0,
"ids": {
"player_token": "{\"u\":123456,\"n\":\"Player1\",\"d\":\"Player\"}"
},
"rows": [
{
"t": 1761560000,
"e": "session_start",
"p": "player_token",
"s": "session_id",
"q": 1,
"v": 42,
"j": "job8char",
"n": 12,
"d": { "is_new": true, "source": "friend", "country": "US" }
}
]
}