Open-source веб-студия для создания игр Рублокса, двойная лицензия AGPL-3.0 + Коммерческая. Главное: - Vite 5 + React 18 + Babylon 7.54.3 + Monaco Editor + Colyseus 0.16 - Самодостаточный движок ~28к строк (66 файлов): BlockManager, TerrainVoxelBuilder, ModelManager, DecoManager, PlayerController, ScriptSandboxWorker, MultiplayerSync, 30+ GD-гейммодов - Главный редактор KubikonEditor (~37к строк) + панели, ScriptEditor (Monaco) - Витрина игр (KubikonFeed, KubikonStudio, KubikonDocs, KubikonLearn) - Geometry Dash sub-app (GdMenu, GdShop, GdRules, GdCoverArt) - 10 admin-preview каталогов для дизайнеров (скины, музыка, порталы и т.д.) - Конфигурируемый бэкенд через VITE_API_BASE — работает со staging (dev-api.rublox.pro) без настройки - Standalone-режим (VITE_STANDALONE=true) — открыть пустой редактор без бэка - Полная документация (на русском): README, ARCHITECTURE, CONTRIBUTING, SECURITY, CHANGELOG - ESLint + Prettier + EditorConfig - Legal: LICENSE (AGPL-3.0), LICENSE-COMMERCIAL.md, CLA.md, COPYRIGHT.md - Issue templates: bug_report, feature_request, security_disclosure Перед публикацией: - Все импорты из minecraftia заменены на локальные - Все хардкоды URL (minecraftia-school.ru) и внутренних IP убраны → env - Admin-эндпоинты Kubikon3DService вырезаны (остаются в приватном репо) - AdminKubikonModeration не публикуется (модерация — в team.rublox.pro) - 93 МБ ассетов public/kubikon-assets вынесены в .gitignore (раздаются через release artifact)
166 lines
9.7 KiB
Markdown
166 lines
9.7 KiB
Markdown
# Архитектура студии Рублокса
|
||
|
||
Как редактор превращает действия мыши в воксельный мир, GLB-модели, скрипты на JS — и сохраняет в JSON для запуска в плеере. Чтение ~10 минут.
|
||
|
||
## Общий поток (открытие проекта)
|
||
|
||
```
|
||
URL = /edit/<gameId>
|
||
│
|
||
▼
|
||
AuthProvider читает JWT из localStorage
|
||
│
|
||
▼
|
||
KubikonEditor.jsx (~3500 строк) главный контейнер, useParams().id = projectId
|
||
│
|
||
├── GET /api-storys/kubikon3d/projects/{id} → project_data (JSON)
|
||
│
|
||
▼
|
||
BabylonScene.create() создание Babylon engine, scene, lights, skybox
|
||
│
|
||
▼
|
||
GameRuntime.loadProject() парсит project_data, спавнит:
|
||
│ - BlockManager.placeBlock() × N (воксели)
|
||
│ - ModelManager.spawnModel() × N (GLB Kenney)
|
||
│ - DecoManager.placeDeco() × N (трава, камни)
|
||
│ - PrimitiveManager.add() × N (box/sphere/cylinder)
|
||
│ - PlayerController.spawn() (камера-«редактор»)
|
||
│
|
||
▼
|
||
ОСНОВНОЙ UI на сцене:
|
||
│ - TopRibbon (тулбар)
|
||
│ - HierarchyPanel (дерево объектов слева)
|
||
│ - InspectorPanel (свойства справа)
|
||
│ - TerrainPanel (кисти ландшафта)
|
||
│ - ScriptEditor (Monaco)
|
||
│ - ToolboxModal (выбор моделей)
|
||
│ - GuiOverlay/Hotbar (HUD режима игры)
|
||
│
|
||
▼
|
||
ЦИКЛ РЕДАКТИРОВАНИЯ юзер:
|
||
- кликает мышью → SelectionManager
|
||
- drag-and-drop → GizmoManager
|
||
- переключает кисти → TerrainManager
|
||
- пишет скрипт → ScriptEditor + Babylon-эмулятор
|
||
- жмёт «Сохранить» → POST /kubikon3d/projects/:id
|
||
- жмёт «Тест» → embedded preview-player
|
||
```
|
||
|
||
## Главные модули редактора
|
||
|
||
### `editor/KubikonEditor.jsx` (3452 строки)
|
||
|
||
Корневой компонент. Хранит ссылки на scene, engine, runtime, managers. Обрабатывает горячие клавиши, undo/redo через `HistoryManager`. Обвязывает все панели вокруг `<canvas>`.
|
||
|
||
### `editor/engine/` (66 файлов, ~28к строк)
|
||
|
||
Самодостаточный Babylon-движок. Точно такой же как в плеере, но с дополнительными редакторскими функциями:
|
||
|
||
| Менеджер | Файл | Что делает |
|
||
|---|---|---|
|
||
| `BabylonScene` | `BabylonScene.js` | Wrapper над Engine+Scene, освещение |
|
||
| `GameRuntime` | `GameRuntime.js` | Оркестратор: loadProject/start/pause/dispose |
|
||
| `PlayerController` | `PlayerController.js` | R15-персонаж, камеры FPV/TPV, контролы |
|
||
| `BlockManager` | `BlockManager.js` | Воксельные блоки uint16-сетка |
|
||
| `TerrainVoxelBuilder` | `terrain/*` | Greedy meshing для чанков |
|
||
| `ModelManager` | `ModelManager.js` | Загрузка GLB через AssetContainer + кеш |
|
||
| `DecoManager` | `DecoManager.js` | ThinInstances для тысяч пропсов |
|
||
| `PrimitiveManager` | `PrimitiveManager.js` | Box/sphere/cylinder примитивы |
|
||
| `SelectionManager` | `SelectionManager.js` | Клик → выделение → highlight |
|
||
| `GizmoManager` | `GizmoManager.js` | Стрелки перемещения/вращения/масштаба |
|
||
| `HistoryManager` | `HistoryManager.js` | Undo/redo стек |
|
||
| `ScriptSandbox` | `scripts/ScriptSandbox.js` | Запуск user JS в Web Worker |
|
||
| `MultiplayerSync` | `multiplayer/MultiplayerSync.js` | Colyseus 0.16 клиент |
|
||
| `Audio/Weapon/Zombie/Npc/Dynamics` | `*Manager.js` | Игровые менеджеры |
|
||
|
||
### `editor/engine/scripts/ScriptSandbox*.js`
|
||
|
||
Безопасный JS-runtime для пользовательских скриптов. Запускается в Web Worker, без доступа к window/document/fetch. Через postMessage-мост даёт API:
|
||
|
||
```js
|
||
game.onTick((dt) => { ... });
|
||
game.onKey('space', () => { player.jump(); });
|
||
scene.findOne('Cube1').rotateY(0.1);
|
||
ui.set({ score: 42 });
|
||
```
|
||
|
||
### `editor/engine/types/`
|
||
|
||
TypeScript-определения для Monaco-автокомплита. Когда юзер пишет скрипт — IDE показывает все доступные методы.
|
||
|
||
## UI-панели
|
||
|
||
```
|
||
+----------------------------------------------------------+
|
||
| TopRibbon: Файл | Правка | Тест | Опубликовать | Помощь |
|
||
+--------+-----------------------------------+-------------+
|
||
| Hierar | | Inspector |
|
||
| chyPan | | Panel |
|
||
| el | <canvas WebGL> | (свойства |
|
||
| | | выделен- |
|
||
| Дерево | | ного) |
|
||
| объек- | | |
|
||
| тов | | |
|
||
| | | |
|
||
| +-----------------------------------+ |
|
||
| | Hotbar / TerrainPanel (внизу) |
|
||
+--------+-------------------------------------------------+
|
||
```
|
||
|
||
## Скриптовый редактор (Monaco)
|
||
|
||
`editor/ScriptEditor.jsx` запускает Monaco (~5МБ vendor chunk, lazy-loaded). Подгружает .d.ts из `engine/types/` для автокомплита. При сохранении скрипт идёт в `ScriptSandbox`, который запускает Worker и шлёт результат обратно.
|
||
|
||
## Поток данных при сохранении
|
||
|
||
```
|
||
Save кнопка → собрать снимок:
|
||
- blocks[] (uint16 array → RLE-compress → base64)
|
||
- models[] {id, type, x, y, z, rx, ry, rz}
|
||
- primitives[] {id, type, color, size}
|
||
- deco[] {type, instances[]}
|
||
- script.code (текст из Monaco)
|
||
- settings {sky, fog, multiplayer, isGd}
|
||
- spawnPoint {x, y, z}
|
||
→ JSON.stringify
|
||
→ PUT /api-storys/kubikon3d/projects/<id> { project_data: "<json>" }
|
||
```
|
||
|
||
## Чего НЕТ в студии
|
||
|
||
- **Само воспроизведение игры в проде** — это [Плеер](https://git.rublox.pro/rublox/player) (отдельный домен `player.rublox.pro`). В студии есть `preview-player/` для теста «прямо здесь».
|
||
- **Лента / поиск опубликованных игр** — на главном сайте `rublox.pro/app`.
|
||
- **Админка модерации** — в `team.rublox.pro/moderator/*` (приватный фронт команды).
|
||
|
||
## С чего начать новую фичу
|
||
|
||
| Что хочешь добавить | Начни здесь |
|
||
|---|---|
|
||
| Новый тип блока | `editor/engine/CONST/blockTypes.js` |
|
||
| Новая декор-фабрика | `admin-preview/gd<тип>/` (фабрика) + регистрация в DecoManager |
|
||
| Новое API скриптов | `editor/engine/scripts/ScriptSandboxAPI.js` + .d.ts в `engine/types/` |
|
||
| Новая GD-механика | `editor/engine/Gd<тип>.js` + регистрация в `GdGameModeRegistry.js` |
|
||
| Новый UI-блок инспектора | `editor/InspectorPanel.jsx` |
|
||
| Новая кисть ландшафта | `editor/TerrainPanel.jsx` + `TerrainManager.brush*` |
|
||
|
||
## Производительность
|
||
|
||
Главные узкие места (что смотреть первым делом):
|
||
|
||
1. **`game.ui.set()` каждый кадр** — React setState 60Hz убивает FPS. Дросселируй 250мс + diff.
|
||
2. **`scene.findOne()` на старте скрипта** — sceneSnapshot приходит через rAF. Звать в `onTick` или setTimeout(0).
|
||
3. **`blockMaterialDirtyMechanism=true`** в Babylon — НЕ включать (ломает рендер новых мешей).
|
||
4. **GLB через `SceneLoader` вместо `AssetContainer`** — течёт материалами.
|
||
5. **Monaco-instance создаётся повторно при ремаунте** — закешируй ссылку.
|
||
6. **Воксельный rebuild на каждый блок** — батчить через requestIdleCallback.
|
||
|
||
## Связь со студией / плеером / минкой
|
||
|
||
- **Бэкенд один на всех** — `api-storys` микросервис (см. `disaster-recovery/` в приватном репо мейнтейнера).
|
||
- **Авторизация общая** — JWT с rublox.pro и player.rublox.pro работают здесь.
|
||
- **API-контракт описан в `api/Kubikon3DService.js`** — все эндпоинты `/kubikon3d/*`.
|
||
|
||
## Лицензионные заметки для контрибьюторов
|
||
|
||
Контрибьютя, ты соглашаешься лицензировать свои изменения под AGPL-3.0 И предоставляешь мейнтейнеру неисключительную безотзывную лицензию на сублицензирование (см. [CLA.md](./CLA.md)). Это нужно чтобы проект мог продавать коммерческие лицензии корпорациям.
|