Большой консолидирующий коммит после поднятия studio.rublox.pro (28 мая 2026). Содержит изменения которые делались в процессе подготовки прод-окружения: Фиксы импортов после выноса из minecraftia: - Массовая замена путей ../../components → ../components (40+ файлов в src/community/, src/admin-preview/) - Замена ../KubikonEditor/ → ../editor/, ../KubikonStudio/ → ../community/, ../AdminPreview/ → ../admin-preview/ - API.js скопирован из минки целиком (было 8 экспортов, стало 312) - Добавлены PLAYER_URL, MyButton_1, недостающие компоненты - Заменены require() на статические ES-imports в BabylonScene, PrimitiveManager, GameRuntime (Vite не поддерживает CJS require) Структура ассетов: - public/kubikon-templates/ → public/assets/kubikon-templates/ - public/kubikon-learn/ → public/assets/kubikon-learn/ - (код искал в /assets/, файлы лежали без /assets/) Навигация роутов внутри студии: - /kubikon-studio/docs → /docs (90+ навигационных вызовов sed-replaced) - /kubikon-editor/X → /edit/X, /kubikon/play/X → /play/X, /kubikon/gd/X → /gd/X UI: - Новый компонент StudioHeader (61px, как в минке) + копия favicon - WithHeader wrapper в App.jsx для всех страниц кроме fullscreen-редактора/плеера - SSO ticket-flow в AuthContext (auto-redeem #ticket= при загрузке) - Тёмная тема карточек игр в ВИКИ (фон #1c2231 вместо #fff, картинка впритык) Документация: - docs/ONBOARDING.md — путь нового контрибьютора от нуля до PR - docs/TUTORIAL_ADD_SCRIPT_API.md — как добавить game.* API - API_USAGE.md — список эндпоинтов backend - README в подпапках engine/, engine/terrain/, engine/voxel/, engine/robloxterrain/, engine/types/ .gitignore: - public/wiki/ исключён (73МБ PNG, будут на CDN отдельной задачей) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
159 lines
11 KiB
Markdown
159 lines
11 KiB
Markdown
# Движок студии Рублокс
|
||
|
||
Это сердце [studio.rublox.pro](https://studio.rublox.pro). Тут живёт всё: рендер Babylon-сцены, физика, скрипты пользователей, мультиплеер, ландшафт, ассет-менеджмент.
|
||
|
||
Для контрибьютора, который впервые видит этот код — читай этот README сверху вниз, дальше доки по подпапкам.
|
||
|
||
## 1. Слои движка
|
||
|
||
Снизу вверх:
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ React UI (panels, тулбары) │
|
||
├─────────────────────────────────────────────────────┤
|
||
│ GameRuntime ← оркестратор «игрового │
|
||
│ PlayerController режима» (когда игрок │
|
||
│ ScriptSandbox(Worker) жмёт Play в редакторе) │
|
||
├─────────────────────────────────────────────────────┤
|
||
│ Менеджеры объектов сцены │
|
||
│ BlockManager — блоки 1×1×1 │
|
||
│ PrimitiveManager — сферы/цилиндры/конусы │
|
||
│ ModelManager — GLB-модели из библиотеки │
|
||
│ UserModelManager — модели юзера (CAD-редактор) │
|
||
│ NpcManager — NPC и враги │
|
||
│ DecoManager — трава/цветы/деревья │
|
||
├─────────────────────────────────────────────────────┤
|
||
│ Системы поверх сцены │
|
||
│ PhysicsWorld — AABB + интеграция Хейуна │
|
||
│ SelectionManager — клик → подсветка │
|
||
│ GizmoController — translate/rotate/scale gizmos │
|
||
│ HistoryManager — Ctrl+Z / Ctrl+Y │
|
||
│ MultiplayerSync — Colyseus state-sync │
|
||
├─────────────────────────────────────────────────────┤
|
||
│ Низ: BabylonScene = Engine + Scene + Camera │
|
||
│ TerrainManager + voxel/ │
|
||
└─────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
`BabylonScene.js` — точка входа. Создаёт `Engine`, `Scene`, `UniversalCamera` с Roblox-style контролами (ПКМ + WASD-полёт), подключает всех менеджеров.
|
||
|
||
## 2. Как игрок ставит блок (полный путь)
|
||
|
||
Это самый частый кейс. Понимаешь его → понимаешь как работают все остальные менеджеры.
|
||
|
||
```
|
||
1. User жмёт ЛКМ в редакторе
|
||
↓
|
||
2. BabylonScene.onCanvasPointerDown
|
||
- делает ray из камеры через курсор
|
||
- scene.pickWithRay() → выясняет, в какую грань какого меша попали
|
||
↓
|
||
3. Если активен инструмент «Кисть блоков»:
|
||
BlockManager.placeBlock(pos, blockType)
|
||
- вычисляет grid-aligned позицию (округление к 1×1×1)
|
||
- создаёт InstancedMesh от шаблонного меша блока (один draw call на тип)
|
||
- регистрирует в внутренней карте: pos.x|y|z → mesh
|
||
- HistoryManager.push({ action: 'place', pos, type })
|
||
↓
|
||
4. Если активен Multiplayer:
|
||
MultiplayerSync.sendBlockPlaced({ pos, type })
|
||
- летит в Colyseus-комнату
|
||
- сервер броадкастит остальным игрокам
|
||
- у них BlockManager.placeBlock() вызывается снова, но без HistoryManager.push
|
||
```
|
||
|
||
## 3. Менеджеры — зачем их столько
|
||
|
||
Каждый менеджер владеет одной категорией объектов. Менеджеры **не знают друг о друге** — все общаются через `BabylonScene` (он же `scene`-объект, проброшен в каждый менеджер при init).
|
||
|
||
| Менеджер | Что хранит | Ключевой метод |
|
||
|---|---|---|
|
||
| **BlockManager** | блоки grid-aligned | `placeBlock(pos, type)` / `removeBlock(pos)` |
|
||
| **PrimitiveManager** | сферы/цилиндры/конусы любого размера | `addPrimitive({ type, pos, size, color })` |
|
||
| **ModelManager** | GLB-модели (мечи, деревья, машины) | `addModel({ modelId, pos, rotation, scaling })` |
|
||
| **UserModelManager** | модели созданные юзером в встроенном CAD | `addUserModel({ userModelId, pos })` |
|
||
| **NpcManager** | NPC и враги (zombies — отдельный `ZombieManager`) | `spawnNpc({ type, pos })` |
|
||
| **DecoManager** | мелкие воксельные декорации (трава 0.05м) | `paintDeco(pos, brushSize, model)` |
|
||
| **TerrainManager** | гладкий ландшафт (legacy, voxel-режим) | `paintVoxel(pos, material)` |
|
||
| **VoxelWorld** + **VoxelRenderer** | новый chunks-based воксельный движок | `setVoxel(x,y,z, type)` |
|
||
| **FolderManager** | дерево объектов (как Workspace в Roblox) | `createFolder(name, parentId)` |
|
||
| **GuiManager** | UI игры (HUD, меню) | `setUiElement(id, props)` |
|
||
| **InventoryManager** | инвентарь игрока (хотбар, рюкзак) | `addItem(itemId, count)` |
|
||
| **WeaponSystem** | оружие, выстрелы, перезарядка | `equip(weaponId)` / `fire()` |
|
||
| **ScriptSandbox** | JS-скрипты пользователей (см. §5) | `runScript(scriptCode, gameApi)` |
|
||
|
||
## 4. Подпапки
|
||
|
||
- **[terrain/](./terrain/)** — legacy воксельный ландшафт (TerrainManager). Кисти, материалы, plant-кисти. Соединён с `BabylonScene` через `TerrainManager.js` в корне.
|
||
- **[voxel/](./voxel/)** — новый chunks-based движок (16×16×16 чанки + greedy meshing). См. [RUBLOX_VOXEL_ENGINE_PLAN.md](../../../../RUBLOX_VOXEL_ENGINE_PLAN.md). Пока работает параллельно с legacy TerrainManager как shadow-копия для замеров.
|
||
- **[robloxterrain/](./robloxterrain/)** — Roblox-style визуальные эффекты для гладкого ландшафта (vertex AO, slope-darken, distance fog).
|
||
- **[types/](./types/)** — общие type-определения и enum'ы (BlockTypes, PrimitiveTypes).
|
||
|
||
## 5. Скрипты пользователей (sandbox)
|
||
|
||
`ScriptSandbox.js` + `ScriptSandboxWorker.js` — самая опасная часть движка. Тут юзеры пишут JS, который запускается в их браузере при игре.
|
||
|
||
Каждый скрипт запускается **в отдельном Worker** (изоляция: нет доступа к DOM, window, fetch). Общение со сценой — только через `postMessage` и проксированное API `game.*`:
|
||
|
||
```js
|
||
// что доступно скрипту:
|
||
game.player.damage(10)
|
||
game.player.kill()
|
||
game.scene.spawn('zombie', { x: 0, y: 0, z: 5 })
|
||
game.ui.set('score', 42)
|
||
game.broadcast('event', payload) // другим скриптам
|
||
game.onMessage(handler)
|
||
game.onTick(handler) // ~60 раз/сек
|
||
```
|
||
|
||
Полный список API: [reference_kubikon_scripting_api.md](../../../../disaster-recovery/) (приватный, не публикуется в opensource).
|
||
|
||
**Не звать `game.ui.set` в `onTick` без throttle!** React setState на 60Hz убивает FPS. Дросселировать через 250мс. [feedback_kubikon_ui_set_throttle].
|
||
|
||
## 6. Что НЕ трогать (опасные оптимизации)
|
||
|
||
После пары инцидентов задокументировано:
|
||
|
||
- **`scene.blockMaterialDirtyMechanism = true`** — ломает рендер новых мешей (трейсеры пуль, debris). Babylon не пересчитывает uniforms для новых материалов.
|
||
- **`scene.createOrUpdateSelectionOctree()`** в hot path — O(N²) каждый кадр, лагает на 1000+ мешей.
|
||
- **`scene.render()` вручную** — Babylon сам в RequestAnimationFrame, дублирование = double render.
|
||
- **`setInterval(..., 16)`** для игровой логики — используй `scene.onBeforeRenderObservable.add(fn)`, тогда привязано к рендер-циклу.
|
||
|
||
Подробнее в [docs/TUTORIAL_DEBUG_BABYLON.md](../../../docs/TUTORIAL_DEBUG_BABYLON.md).
|
||
|
||
## 7. Game Runtime — особый режим
|
||
|
||
`GameRuntime.js` — когда юзер жмёт «Play» в редакторе. Это не отдельный код-путь, а **набор флагов** на тех же менеджерах:
|
||
|
||
- `PlayerController.spawn()` — создаёт игрового персонажа (R15-скелет) на spawn-блоке
|
||
- `PhysicsWorld.enable()` — включается AABB-симуляция (в редакторе отключена)
|
||
- `ScriptSandbox.startAll()` — запускает все скрипты пользователей
|
||
- `MultiplayerSync.join()` — если игра опубликована, коннектится к Colyseus-комнате
|
||
|
||
Когда юзер жмёт «Stop» — всё откатывается, состояние из HistoryManager восстанавливается.
|
||
|
||
## 8. Производительность — ориентиры
|
||
|
||
| Объект | Сколько ок | Сколько начинает лагать |
|
||
|---|---|---|
|
||
| Блоки (InstancedMesh) | 50 000 | 200 000+ |
|
||
| Примитивы (StandardMesh) | 500 | 2000+ |
|
||
| GLB-модели | 200 | 500+ (зависит от вершин) |
|
||
| NPC | 50 | 100+ (физика дорогая) |
|
||
| Активных скриптов | 30 | 100+ (каждый = Worker = поток) |
|
||
| Частицы | 5000 | 20 000+ |
|
||
|
||
FPS-цель: **60 FPS на средних ноутбуках 2020+**, **30 FPS на школьных машинках 2015+**.
|
||
|
||
## 9. Что почитать дальше
|
||
|
||
- [terrain/README.md](./terrain/README.md) — воксельный ландшафт
|
||
- [voxel/README.md](./voxel/README.md) — новый chunks-движок
|
||
- [../README.md](../README.md) — про папку editor/ выше
|
||
- [docs/TUTORIAL_ADD_BLOCK.md](../../../docs/TUTORIAL_ADD_BLOCK.md) — добавить новый тип блока
|
||
- [docs/TUTORIAL_DEBUG_BABYLON.md](../../../docs/TUTORIAL_DEBUG_BABYLON.md) — отладка Babylon-сцены
|
||
- [docs/TUTORIAL_ADD_SCRIPT_API.md](../../../docs/TUTORIAL_ADD_SCRIPT_API.md) — добавить API в `game.*`
|
||
|
||
Вопросы — в канал `#разработка` на https://team.rublox.pro.
|