studio/rbxl-importer/CHANGELOG.md
min c375ae01ac
All checks were successful
CI / Lint (pull_request) Successful in 1m6s
CI / Build (pull_request) Successful in 2m2s
CI / Secret scan (pull_request) Successful in 26s
CI / PR size check (pull_request) Successful in 7s
CI / Deploy to S1 + S2 (pull_request) Has been skipped
feat(rbxl-import): импорт Roblox .rbxl карт в Rublox-проекты
Тест-фича для МИНа. Полное описание в rbxl-importer/INFO_PROCESS.md.

Backend (rbxl-importer/ на VM 130 S1):
- Python-парсер Roblox Binary (28+ типов значений)
- Asset downloader через Marfusha proxy + .ROBLOSECURITY cookie
- Mesh→GLB конвертер (v1-v5)
- Converter Roblox-классов → project_data
- Flask API: /analyze + /create

Frontend:
- API.js + components/RbxlImportModal.jsx (drag-n-drop)

Тестовый импорт Easy Obby: project_id 2697,
2244 primitives + 742 lua-scripts + 5 ассетов.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 18:24:27 +03:00

101 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# rbxl-importer: лог разработки
## 2026-06-07
### Фаза 0. Подготовка (✓)
- Освобождено место на S1: удалён `pve/data` LV (+133 GB), VM 111/112/114/116 (+285 GB). Свободно стало 419 GB в VG `pve`.
- Создана **VM 130 rbxl-importer** (IP 192.168.1.130, Ubuntu 22.04, 4 vCPU, 4 GB RAM, 200 GB).
- Установлены: Docker, Python 3.11+venv, nginx, postgresql-client.
- Клонированы **studio-rbxl-import** и **player-rbxl-import** worktree, ветка `feat/rbxl-import`.
- Smoke-test парсера на `Escape Easy Obby Parkour Uncopylocked.rbxl` (8205 instances, 120 классов).
### Фаза 1. Парсер `.rbxl` (✓)
- Реализованы файлы: `rbxl_binreader.py`, `rbxl_types.py`, `rbxl_parser.py`.
- Декодирование 28+ Roblox PROP типов: String, Bool, Int32, Float, Double, UDim, UDim2, Ray, Faces, Axes, BrickColor, Color3, Vector2, Vector3, CFrame, Quaternion, Enum, Referent, Vector3int16, NumberSequence, ColorSequence, NumberRange, Rect, PhysicalProperties, Color3uint8, Int64, SharedString, Bytecode, OptionalCFrame, UniqueId, Font.
- Особенности формата покрыты: interleaved-transformed массивы, zigzag для signed int, Roblox float encoding, LZ4 chunks.
- **Протестировано на 6 файлах: 0 warnings**:
- `easy_obby.rbxl` (Easy Obby Parkour, 437 KB, 8205 instances)
- `miners-haven.rbxl` (Miners Haven, 8 MB, **60950 instances**)
- 4 synthetic из `rojo-rbx/rbx-dom/benches/files/`
### Фаза 2. Asset pipeline (✓)
- БД: миграция `001_roblox_assets.sql` (3 таблицы) применена в `storys_db` (S2 primary через autossh туннель S1 PVE 192.168.1.152:25435).
- `asset_downloader.py`: дедупликация по `rbx_asset_id` + sha256, retry с backoff, классификация по content-type/magic bytes.
- `asset_proxy.py`: режимы `disabled` / `direct` / `http_proxy` / `cloudflare_worker`. Используется `http_proxy` через Marfusha xray (85.192.61.244:39237).
- **Cookie auth**: `.ROBLOSECURITY` от аккаунта `minkorenovsk2` сохранён в `/home/min/.roblosecurity` на VM 130, EnvironmentFile подключен в systemd unit.
- `mesh_converter.py`: парсер Roblox `.mesh` v1-v5 + GLB writer (glTF 2.0 binary).
- **v1.00** ASCII протестирован: 500 facets, 1500 vertices → 54900 байт GLB.
- v2-v5 binary — написаны, проверим на реальных файлах.
- `nginx` на VM 130: `/opt/roblox-assets/` отдаётся как `https://assets.rublox.pro/roblox/...` с CORS.
### Фаза 3. Конвертер геометрии (✓)
- `converter.py`: маппинг 30+ Roblox-классов → Rublox `project_data`.
- `Part`, `WedgePart`, `CornerWedgePart`, `TrussPart` → primitives (cube/wedge/cornerwedge).
- `MeshPart`, `UnionOperation` → glbModels (с fallback на bbox cube).
- `SpawnLocation` → scene.spawnPoint.
- `Lighting` → scene.environment.
- `Sound` → scene.sounds.
- `Script`/`LocalScript`/`ModuleScript` → scene.scripts с kind='roblox-lua' и raw lua_source.
- Material enum (Plastic→glossy, Neon→neon, Metal→metal, Glass→glass, ...).
- CFrame → position + Euler XYZ (system axes Roblox = Babylon: right-handed Y-up).
- Scale: 1 Roblox stud = 0.28 м (настраиваемо).
- **Easy Obby результат**: 2244 primitives + 742 lua-scripts + 5 ассетов (sounds) для скачки.
### Фаза 4. Lua-runtime + Roblox API shim (✓)
- **wasmoon** (Lua 5.4 WASM) интегрирован в `player/studio` (npm install).
- `RobloxLuaWorker.js` — Worker-хост с инициализацией wasmoon, IPC с main thread.
- `RobloxLuaSandbox.js` — main-side обёртка.
- `roblox-shim.js` — math классы (Vector3, Color3, CFrame, UDim2), Instance прокси (game, workspace, script, GetService, GetChildren, FindFirstChild, IsA с иерархией классов), Part свойства (Position/CFrame/Size/Color/Material/Anchored/CanCollide/Transparency), RBXScriptSignal (Touched, Heartbeat, Stepped, RenderStepped, Connect, Wait, Disconnect).
- `roblox-scheduler.js` — корутины через `coroutine.create/resume/yield`, шедулер для wait/task.wait/task.delay/task.spawn, автоматический fire Heartbeat/Stepped/RenderStepped на каждом tick.
- `roblox-tween.js` — TweenService с 10 easing-функциями (Linear, Quad, Cubic, Quart, Quint, Sine, Bounce, Elastic, Back, Exponential) для Vector3/Color3/CFrame/number.
- `roblox-services.js` — Players, LocalPlayer, Character, Humanoid (Health, WalkSpeed, JumpPower, TakeDamage, Died), UserInputService, RemoteEvent (FireServer/FireClient/OnServerEvent), RemoteFunction, DataStoreService (GetAsync/SetAsync/IncrementAsync), HttpService (JSONEncode/Decode), ContextActionService stub.
- `roblox-physics.js` — BodyVelocity, BodyGyro, BodyPosition, BodyForce, BodyAngularVelocity, AlignPosition, LinearVelocity.
### Тесты Lua-runtime: **36/36 ✓**
- `tests/rbxl-lua-mvp.test.js` — math + Instance + Part + IsA (**9/9**)
- `tests/rbxl-lua-wait.test.js` — корутины + wait/task.wait/task.delay (**5/5**)
- `tests/rbxl-lua-tween.test.js` — TweenService + Linear easing (**2/2**)
- `tests/rbxl-lua-services.test.js` — Humanoid + DataStore + HttpService + RemoteEvent (**8/8**)
- `tests/rbxl-lua-integration.test.js` — реалистичные obby/simulator снейппеты (**12/12**):
KillBrick, WalkSpeed boost, Tween door, BodyVelocity конвейер, leaderstats, DataStore checkpoint, циклы с wait, task.spawn параллель, Color3 + Material смена, RemoteEvent client→server, Heartbeat счётчик, Vector3 arithmetic.
### Фаза 5. Flask API + UI (✓)
- `src/app.py` Flask:
- `GET /health` → ok
- `POST /import/rbxl/analyze` → парсер + report + preview_hash (Redis 20 мин TTL)
- `POST /import/rbxl/create` → скачка ассетов + конверт mesh→GLB + INSERT в kubikon3d_projects
- Запущен через **systemd unit** `rbxl-importer.service` (Restart=on-failure, EnvironmentFile с cookie).
- Redis (Docker `redis-rbxl`) для preview cache.
- `studio/src/components/RbxlImportModal.jsx` — React компонент с drag-n-drop, отчётом, формой создания. Доступен только МИНу.
- `studio/src/api/rbxlImporterApi.js` — клиент.
- Тест-результат: **Easy Obby импортирован как project_id 2697** (2244 primitives, 742 lua-scripts, 5 ассетов скачано без ошибок).
### Фаза 6. Совместимость с плеером + DNS (✓)
- `GameRuntime.js`: добавлен `_startRobloxLuaScript()` метод и ветка `if (s.kind === 'roblox-lua')` в `start()`.
- `_handleRobloxLuaCommand()`: маппит IPC команды от Lua-sandbox (partSet, partVel, playerCmd) на PrimitiveManager и game.player API.
- `_buildRobloxLuaSceneSnap()`: преобразует projectData.scene.primitives → формат для Lua (workspace:GetChildren).
- **NPM proxy_host** на S1 NPM (VM 101 192.168.1.43): `assets.rublox.pro` и `api-rbxl.rublox.pro` → VM 130:80.
- **DNS Cloudflare**: 2 A-записи (proxied=false) → 85.175.7.40 (S1 публичный IP).
- **End-to-end протестировано**: `https://api-rbxl.rublox.pro/health` → 200 OK.
### Фаза 7. Документация (в работе)
- README.md в rbxl-importer/
- INFO_PROCESS.md (этот файл)
- TODO: commit + PR в Gitea для studio и player worktree.
## Известные ограничения
- Lua-runtime пока MVP: нет GUI (ScreenGui/Frame/TextLabel), нет `:Wait()` на сигналах через корутины (только `Connect`), нет Animation/KeyframeSequence.
- CSG meshes (UnionOperation) парсятся, но конверт в GLB не реализован — bbox cube fallback.
- Terrain voxel grid конвертится в заглушку (плоский ландшафт).
- TouchEvent в плеере не fire'ится автоматически из физики Babylon — нужно добавить collision broadcaster.