studio/src/editor/engine/GdPortalArch.js
МИН 61fba4e174
Some checks failed
CI / Lint + Format (push) Failing after 32s
CI / Build (push) Failing after 37s
CI / Secret scan (push) Failing after 37s
CI / PR size check (push) Has been skipped
fix: починка билда + studio.rublox.pro инфра
Большой консолидирующий коммит после поднятия studio.rublox.pro (28 мая 2026).
Содержит изменения которые делались в процессе подготовки прод-окружения:

Фиксы импортов после выноса из minecraftia:
- Массовая замена путей ../../components → ../components (40+ файлов в src/community/, src/admin-preview/)
- Замена ../KubikonEditor/ → ../editor/, ../KubikonStudio/ → ../community/, ../AdminPreview/ → ../admin-preview/
- API.js скопирован из минки целиком (было 8 экспортов, стало 312)
- Добавлены PLAYER_URL, MyButton_1, недостающие компоненты
- Заменены require() на статические ES-imports в BabylonScene, PrimitiveManager, GameRuntime (Vite не поддерживает CJS require)

Структура ассетов:
- public/kubikon-templates/ → public/assets/kubikon-templates/
- public/kubikon-learn/ → public/assets/kubikon-learn/
- (код искал в /assets/, файлы лежали без /assets/)

Навигация роутов внутри студии:
- /kubikon-studio/docs → /docs (90+ навигационных вызовов sed-replaced)
- /kubikon-editor/X → /edit/X, /kubikon/play/X → /play/X, /kubikon/gd/X → /gd/X

UI:
- Новый компонент StudioHeader (61px, как в минке) + копия favicon
- WithHeader wrapper в App.jsx для всех страниц кроме fullscreen-редактора/плеера
- SSO ticket-flow в AuthContext (auto-redeem #ticket= при загрузке)
- Тёмная тема карточек игр в ВИКИ (фон #1c2231 вместо #fff, картинка впритык)

Документация:
- docs/ONBOARDING.md — путь нового контрибьютора от нуля до PR
- docs/TUTORIAL_ADD_SCRIPT_API.md — как добавить game.* API
- API_USAGE.md — список эндпоинтов backend
- README в подпапках engine/, engine/terrain/, engine/voxel/, engine/robloxterrain/, engine/types/

.gitignore:
- public/wiki/ исключён (73МБ PNG, будут на CDN отдельной задачей)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 05:01:13 +03:00

134 lines
6.0 KiB
JavaScript
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.

/**
* GdPortalArch — рендер 3D-арок поверх портал-примитивов (gd_ship, gd_cube, …).
*
* Работа:
* 1. attach(scene, scene3d) пробегает по primitiveManager.instances.
* 2. Для каждого primitive с typeDef.kind === 'gd_portal':
* - Читает выбор юзера: gd_portal_choices[gdMode] (например 'ship_v3').
* - Если выбора нет — берёт дефолт `${gdMode}_v1`.
* - Спавнит арку из PORTAL_CATALOG в той же позиции.
* - Скрывает оригинальный mesh (visible=false), но НЕ удаляет — GdLevelManager
* читает позицию из primitiveManager и обрабатывает коллизию.
*
* Загрузка выбора:
* GET /api/v1/storys/kubikon3d/savegame/295/<userId>/gd_portal_choices
* data = { cube: 'cube_v5', ship: 'ship_v5', ... }
*
* Дефолты (если нет выбора):
* все группы = `${gdMode}_v1` (классический).
*/
import { Vector3, TransformNode } from '@babylonjs/core';
import { PORTAL_CATALOG } from '../../admin-preview/gdPortals/portalFactories';
import { STORYS_addres as STORYS_BASE } from '../../api/API';
// project_id выбора порталов = 295 (sandbox-проект, где юзер выбирал)
const PORTAL_CHOICES_PID = 295;
const PORTAL_CHOICES_NS = 'gd_portal_choices';
const DEFAULT_PORTAL_BY_MODE = {
cube: 'cube_v1',
ship: 'ship_v1',
ball: 'ball_v1',
ufo: 'ufo_v1',
wave: 'wave_v1',
robot: 'robot_v1',
};
export class GdPortalArch {
constructor() {
this.scene = null;
this._scene3d = null;
this._instances = []; // [{ portalPrimId, handle }]
this._choices = null;
}
/**
* Привязать к сцене.
* userId — для загрузки выбора порталов из savegame (project_id=295, ns=gd_portal_choices).
* Если userId не передан или загрузка не удалась — используются дефолты `${mode}_v1`.
*/
async attach(scene, scene3d, userId = null) {
if (!scene || !scene3d) return;
this.scene = scene;
this._scene3d = scene3d;
this._choices = {};
if (userId) {
try {
const url = `${STORYS_BASE}/kubikon3d/savegame/${PORTAL_CHOICES_PID}/${userId}/${PORTAL_CHOICES_NS}`;
const r = await fetch(url, {
headers: { Authorization: localStorage.getItem('Authorization') || '' },
});
if (r.ok) {
const j = await r.json();
this._choices = j?.data || {};
}
} catch (e) {
console.warn('[GdPortalArch] не загрузил выбор:', e);
}
}
// Откладываем — примитивы могут быть не созданы в момент enterPlayMode
let attempts = 0;
const tryRender = () => {
attempts++;
const pm = scene3d.primitiveManager;
if (!pm || !pm.instances) {
if (attempts < 10) setTimeout(tryRender, 200);
return;
}
this._renderAll();
};
tryRender();
}
_renderAll() {
const pm = this._scene3d.primitiveManager;
if (!pm || !pm.instances) return;
let rendered = 0;
for (const data of pm.instances.values()) {
// type у primitive — строка вроде 'gd_ship', 'gd_cube'.
// У этих типов в PrimitiveTypes.js kind = 'gd_portal' и есть gdMode.
// Импортировать getPrimitiveType из engine — там есть.
// Но проще проверить по type-строке:
const portalType = data.type || '';
const m = portalType.match(/^gd_(\w+)$/);
if (!m) continue;
const gdMode = m[1]; // 'cube', 'ship', …
if (!DEFAULT_PORTAL_BY_MODE[gdMode]) continue; // не порталовый
const chosenId = (this._choices && this._choices[gdMode]) || DEFAULT_PORTAL_BY_MODE[gdMode];
const factory = PORTAL_CATALOG.find(p => p.id === chosenId);
if (!factory) {
console.warn('[GdPortalArch] не найдена фабрика:', chosenId);
continue;
}
try {
const handle = factory.make(this.scene, `portal_inst_${data.id}`);
if (!handle || !handle.root) continue;
// Низ колонн на y=0; ставим на y=1 (поверх пола), как GdStartArch
handle.root.position = new Vector3(data.x, 1, data.z);
// Поворот по Y 90° — фабрики строят арку проёмом вдоль X (колонны на ±X).
// Игрок бежит по +X — арка должна стоять поперёк, колонны на ±Z, проём вдоль X.
handle.root.rotation.y = Math.PI / 2;
// Скрыть оригинальный primitive-меш (GdLevelManager всё ещё видит data.x для коллизии)
if (data.mesh) {
data.mesh.setEnabled(false);
}
this._instances.push({ portalPrimId: data.id, handle });
rendered++;
} catch (e) {
console.warn('[GdPortalArch] ошибка при создании арки:', chosenId, e);
}
}
console.log(`[GdPortalArch] отрисовано ${rendered} порталов`);
}
dispose() {
for (const inst of this._instances) {
try { inst.handle && inst.handle.dispose && inst.handle.dispose(); } catch (e) {}
}
this._instances = [];
this.scene = null;
this._scene3d = null;
}
}