All checks were successful
Сегодня доведены до играбельного состояния:
- UI модалка импорта подключена в KubikonStudio (кнопка для МИНа в навигации)
- Converter: SCALE 0.35 (карта пропорциональна R15-персонажу),
playerModelType='skin_bacon-hair', Lua упакован в поле code с маркером
// @roblox-lua (storys API сохраняет только {id,code,target,name})
- vite.config: api+статика через rublox.pro/minecraftia-school.ru
- GameRuntime: распознаёт маркер, запускает через RobloxLuaSandbox
+ wasmoon Worker. Фильтрация: target!=null + lua<2500б +
лимит 50 sandbox'ов (WASM OOM при >50 VM)
- roblox-shim: nullStub (Proxy с no-op методами) вместо null
для FindFirstChild/WaitForChild — цепочки не падают
- require() заменён на nullStub
- RobloxLuaSandbox: совместимость с интерфейсом ScriptSandbox
(sendGlobalEvent/SceneSnapshot/etc — no-op заглушки)
- RobloxLuaWorker: pcall обёртка над user-кодом
- remoteDevlog.js + /devlog endpoint: автосбор browser-логов
- PlayerController._loadSkinManifest: dev-fallback на studio.rublox.pro
Тест на Easy Obby:
- 8205 instances → 2245 primitives + 742 Lua-scripts
- 50/742 Lua-VM запущены (KillBrick handlers и т.п.),
151 отфильтровано как admin/chat services, 541 пропущено по памяти
- Скин bacon-hair виден, FPS 20-25
- Сцена играется, можно ходить, прыгать
TODO (следующая итерация):
- Single-VM mode для wasmoon (один Lua-state на 742 скрипта,
убрать WASM OOM)
- Реализовать select/focus в иерархии для импортированных карт
- Touched events от Babylon impostor → Lua-shim сигналы
- Поддержка GUI (ScreenGui/Frame/TextLabel) в конвертере
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
66 lines
2.1 KiB
Python
66 lines
2.1 KiB
Python
"""
|
||
app_devlog.py — endpoint /devlog для удалённого сбора логов dev-сессии студии.
|
||
|
||
Подключается к Flask из app.py через blueprint.
|
||
|
||
Эндпоинты:
|
||
POST /devlog — приём batch'а событий из браузера
|
||
GET /devlog/recent — последние N событий (для меня)
|
||
GET /devlog/clear — очистить лог
|
||
|
||
Хранение: append-only файл /opt/rbxl-importer/devlog.jsonl.
|
||
|
||
CORS открыт для localhost:* (dev режим).
|
||
"""
|
||
import os
|
||
import json
|
||
import time
|
||
from flask import Blueprint, request, jsonify
|
||
|
||
devlog_bp = Blueprint('devlog', __name__)
|
||
|
||
DEVLOG_PATH = os.environ.get('DEVLOG_PATH', '/opt/rbxl-importer/devlog.jsonl')
|
||
|
||
|
||
@devlog_bp.post('/devlog')
|
||
def post_devlog():
|
||
"""Принимает массив событий из браузера.
|
||
|
||
Каждое событие: {ts, kind, url?, status?, body?, message?, stack?, extra?}
|
||
"""
|
||
data = request.get_json(silent=True) or {}
|
||
events = data.get('events') or []
|
||
if not isinstance(events, list):
|
||
return jsonify({'error': 'events must be list'}), 400
|
||
received_at = time.time()
|
||
with open(DEVLOG_PATH, 'a', encoding='utf-8') as f:
|
||
for ev in events:
|
||
if not isinstance(ev, dict):
|
||
continue
|
||
ev['received_at'] = received_at
|
||
f.write(json.dumps(ev, ensure_ascii=False, default=str) + '\n')
|
||
return jsonify({'ok': True, 'count': len(events)})
|
||
|
||
|
||
@devlog_bp.get('/devlog/recent')
|
||
def get_recent():
|
||
"""Последние N событий (по умолчанию 200)."""
|
||
n = int(request.args.get('n', 200))
|
||
out = []
|
||
if os.path.exists(DEVLOG_PATH):
|
||
with open(DEVLOG_PATH, 'r', encoding='utf-8') as f:
|
||
lines = f.readlines()[-n:]
|
||
for line in lines:
|
||
try:
|
||
out.append(json.loads(line))
|
||
except Exception:
|
||
pass
|
||
return jsonify({'events': out, 'total': len(out)})
|
||
|
||
|
||
@devlog_bp.get('/devlog/clear')
|
||
def clear():
|
||
if os.path.exists(DEVLOG_PATH):
|
||
os.unlink(DEVLOG_PATH)
|
||
return jsonify({'ok': True})
|