# 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.
