Юзер указал что вся документация opensource-репо должна быть на русском. Также: - .env.example комменты на русском - package.json description на русском - Описания org и repo в Gitea обновлены через API
146 lines
9.3 KiB
Markdown
146 lines
9.3 KiB
Markdown
# Архитектура плеера Рублокса
|
||
|
||
Как 3D-игра загружается, рендерится и синхронизируется. Чтение ~5 минут.
|
||
|
||
## Общий поток
|
||
|
||
```
|
||
URL = /<gameId> например /265
|
||
│
|
||
▼
|
||
PlayerAuthProvider проверяет JWT в localStorage["player_jwt"]
|
||
│ ИЛИ обменивает URL #ticket=... на JWT
|
||
▼
|
||
useAuth().isAuthenticated
|
||
│
|
||
▼
|
||
KubikonPlayer.jsx главный контейнер, читает {projectId} из useParams
|
||
│
|
||
├── GET /api-storys/kubikon3d/projects/{id} → project_data (JSON)
|
||
│
|
||
▼
|
||
BabylonScene.create() создание движка Babylon, сцены, света, неба
|
||
│
|
||
▼
|
||
GameRuntime.loadProject() парсинг project_data, инстанциация всего:
|
||
│ - BlockManager.placeBlock() × N (Minecraft-блоки)
|
||
│ - ModelManager.spawnModel() × N (Kenney GLB)
|
||
│ - DecoManager.placeDeco() × N (декор ландшафта)
|
||
│ - PrimitiveManager.add() × N (кубы/сферы/цилиндры)
|
||
│ - PlayerController.spawn() (R15-персонаж + камера)
|
||
│
|
||
▼
|
||
ScriptSandboxWorker пользовательские JS-скрипты в Web Worker-песочнице.
|
||
│ Доступный API: game.player, scene, ui, broadcast.
|
||
│
|
||
▼
|
||
MultiplayerSync опционально. Колyseus-комната, синк позиций.
|
||
│
|
||
▼
|
||
ЦИКЛ РЕНДЕРА (60 fps) scene.render() каждый кадр
|
||
```
|
||
|
||
## Ключевые модули
|
||
|
||
### `engine/BabylonScene.js`
|
||
|
||
Обёртка над `Engine` + `Scene` из Babylon. Создаёт освещение (HemisphericLight + DirectionalLight + теневой генератор), скайбокс, туман. Единый источник правды для ссылки на `scene`.
|
||
|
||
### `engine/GameRuntime.js`
|
||
|
||
Оркестратор. Читает `project_data` (JSON, сохранённый редактором студии) и направляет каждый элемент в соответствующий менеджер. Lifecycle-хуки: `loadProject()`, `start()`, `pause()`, `dispose()`.
|
||
|
||
Также обрабатывает «external URL» резолвинг (`_resolveExternalUrl`) — скрипты игры могут вызвать `game.openUrl('/kubikon/play/12')`, и URL корректно резолвится в сам плеер или на главный сайт.
|
||
|
||
### `engine/PlayerController.js`
|
||
|
||
R15-персонаж (15-костный Mixamo-rig). Камеры от первого и третьего лица. Управление WASD/тач. Прыжки, гравитация, столкновения с воксельным гридом + AABB-моделями. Спавн/респавн.
|
||
|
||
Спец-режимы для GD: `setAutoRun(true)`, `setShipMode(true)` — используются гейммодами Geometry Dash (куб/корабль/волна/НЛО/мяч/паук).
|
||
|
||
### `engine/BlockManager.js` / `TerrainVoxelBuilder.js`
|
||
|
||
Воксельный ландшафт. Блоки — uint16 type-id'ы в чанковых массивах. `rebuildChunk(chunkId)` делает greedy meshing → один меш на материал на чанк (~40-100× меньше draw call'ов чем наивно).
|
||
|
||
### `engine/ModelManager.js`
|
||
|
||
Загрузка `.glb`-моделей Kenney (или загруженных дизайнерами GLB из `/api-storys/assets/rublox-designer/models/...`). Кеширует `AssetContainer` по `modelTypeId`, инстанциирует клоны при каждом спавне.
|
||
|
||
### `engine/DecoManager.js`
|
||
|
||
Лёгкие декоративные пропсы (камни, растения, знаки). Использует `ThinInstanceCount` для огромного перфоманса (тысячи инстансов → один draw call на тип).
|
||
|
||
### `engine/scripts/ScriptSandboxWorker.js`
|
||
|
||
Пользовательские JS-скрипты выполняются в отдельном Web Worker. Доступная API-поверхность (только чтение):
|
||
|
||
```js
|
||
// В скрипте игрока:
|
||
game.onTick((dt) => { ... });
|
||
game.onKey('space', () => { player.jump(); });
|
||
game.broadcast('event', payload);
|
||
game.player.setHealth(100);
|
||
scene.findOne('Cube1').rotateY(0.1);
|
||
ui.set({ score: 42 });
|
||
```
|
||
|
||
Скрипты **НЕ имеют** доступа к `window`, `document`, `fetch`, `localStorage`, сети. Запускаются изолированно в Worker'е через строгий postMessage-мост.
|
||
|
||
### `engine/multiplayer/MultiplayerSync.js`
|
||
|
||
Colyseus 0.16 клиент. Подключается к комнате per `gameId` если мультиплеер включён. Синхронизирует позиции/повороты/анимации игроков на 20 Hz. Сервер по `VITE_REALTIME_WS`.
|
||
|
||
### `engine/gd/*` (модули Geometry Dash)
|
||
|
||
30+ классов реализующих GD-стиль 2D-автораннер-гейммоды в 3D-мире:
|
||
- `GdCube.js` — обычный прыжок
|
||
- `GdShip.js` — гравитация-флип, полёт
|
||
- `GdWave.js` — синусоида-дэш
|
||
- `GdBall.js` — флип-прыжок мячом
|
||
- `GdUfo.js` — мульти-тап прыжки
|
||
- `GdSpider.js` — мгновенный телепорт
|
||
- + порталы, ускорители, шипы, финишные линии, трейлы, чекпойнт-музыка
|
||
|
||
Фабрики для каждого (`GdSpikeFactory`, `GdPortalFactory`, `GdMusicFactory`...) живут в `AdminPreview/gd*/`.
|
||
|
||
## Поток данных (один кадр)
|
||
|
||
```
|
||
1. ВВОД клавиатура/тач/геймпад → PlayerController.onInput()
|
||
2. ФИЗИКА гравитация + скорость + столкновения с блоками/моделями
|
||
3. СКРИПТЫ onTick(dt) колбэки пользовательских скриптов (в Worker'е, асинхронно)
|
||
4. МУЛЬТИПЛЕЕР читаем удалённые позиции, lerp других игроков
|
||
5. РЕНДЕР Babylon scene.render() → WebGL2 draw calls
|
||
6. UI React ре-рендерится только если вызвали game.ui.set() (throttle 250мс)
|
||
```
|
||
|
||
## Чего НЕТ в плеере
|
||
|
||
- **Редактирование** — это в [Студии](https://git.rublox.pro/rublox/studio).
|
||
- **Лента игр / поиск / публикация** — это на главном сайте (`rublox.pro/app`).
|
||
- **Авторизация-UI** — игроки приходят с JWT/ticket. Плеер только читает их.
|
||
- **Админка / модерация** — вынесены в приватный репозиторий (`disaster-recovery/` для мейнтейнера).
|
||
|
||
## Узкие места по производительности (что смотреть первым делом если тормозит)
|
||
|
||
1. **`game.ui.set()` вызывается каждый кадр** — React setState 60Hz убивает FPS. Дросселируй до 250мс с diff-проверкой.
|
||
2. **`scene.findOne()` на старте** — `sceneSnapshot` приходит через rAF; ссылка будет null. Перенеси в `onTick` или `setTimeout(0)`.
|
||
3. **`blockMaterialDirtyMechanism=true` в Babylon** — НЕ включать. Ломает рендер новых мешей (debris, трейсеры).
|
||
4. **`createOrUpdateSelectionOctree()`** — то же. Ломает превью-призраки редактора.
|
||
5. **GLB-загрузка через `SceneLoader` вместо `AssetContainer`** — течёт материалами. Используй `AssetContainer` + `instantiateModelsToScene()`.
|
||
|
||
## С чего начать новую фичу
|
||
|
||
| Что хочешь добавить | Начни здесь |
|
||
|---|---|
|
||
| Новый тип блока | `engine/CONST/blockTypes.js` + текстура в `public/kubikon-assets/blocks/` |
|
||
| Новое API для скриптов | `engine/scripts/ScriptSandboxAPI.js` (добавь в `apiSurface`) |
|
||
| Новый GD-гейммод | Скопируй `engine/gd/GdBall.js`, зарегистрируй в `GdGameModeRegistry.js` |
|
||
| Новый HUD-виджет | `editor-shared/GameHud.jsx` |
|
||
| Превью-роут для дизайнера | `AdminPreview/` (например `gdSkins/PreviewGdSkins.jsx`) |
|
||
| Мультиплеер-событие | Colyseus-схема в `engine/multiplayer/schemas/` + хендлер в `MultiplayerSync.js` |
|
||
|
||
## Лицензионные заметки для контрибьюторов
|
||
|
||
Контрибьютя, ты соглашаешься лицензировать свои изменения под AGPL-3.0 И предоставляешь мейнтейнеру неисключительную безотзывную лицензию на сублицензирование (см. [CLA.md](./CLA.md)). Это нужно чтобы проект мог продавать коммерческие лицензии корпорациям.
|