- game.fx.pointer + расширенный game.fx.beam: BeamManager (текстуры/curved/ градиент/quest-marker), ScriptSandboxWorker (_normFxPoint от DataCloneError), GameRuntime (fx.createPointer/pointerTarget/pointerUpdate/beamUpdate/ beamVisible), BabylonScene._activatePointers. 1-в-1 со студией. - Dev JWT-панель на экране «Нужен JWT» (только localhost): кнопка → инпут → localStorage.player_jwt + reload. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
93 lines
4.1 KiB
JavaScript
93 lines
4.1 KiB
JavaScript
// Этап 2: полный flow авторизации через ticket.
|
||
//
|
||
// Route /:id — единственный полезный, обработка отсутствия gameId через
|
||
// "*" (показываем LoadingScreen с сообщением).
|
||
//
|
||
// Цепочка состояний:
|
||
// 1. PlayerAuthProvider грузит JWT/ticket → isLoading=true
|
||
// 2. isLoading=false, error='no_jwt_local' (только localhost) → подсказка
|
||
// про DevTools
|
||
// 3. isAuthenticated=true → рендерим KubikonPlayer
|
||
// 4. Любая redeem-ошибка → "Сессия истекла. Вернись на rublox.pro" с
|
||
// кнопкой возврата
|
||
//
|
||
// На проде (player.rublox.pro) шаг 2 не наступает — там redirect на
|
||
// rublox.pro/app сразу.
|
||
|
||
import { BrowserRouter, Routes, Route, useParams } from 'react-router-dom';
|
||
import { PlayerAuthProvider, useAuth } from './auth/PlayerAuth';
|
||
import KubikonPlayer from './KubikonPlayer/KubikonPlayer';
|
||
import LoadingScreen from './LoadingScreen';
|
||
import { RUBLOX_HOME } from './api/API';
|
||
// Подфаза 3.4 RUBLOX_ACCESSORY_ATTACHMENT_PLAN.md
|
||
import PreviewSkinRoute from './PreviewSkin/PreviewSkinRoute';
|
||
// Фаза 5.6 RUBLOX_DESIGNER_PLAN.md — preview модели
|
||
import PreviewModelRoute from './PreviewSkin/PreviewModelRoute';
|
||
// 2026-05-27 — preview аватара
|
||
import PreviewAvatarRoute from './PreviewSkin/PreviewAvatarRoute';
|
||
// 6.7 RUBLOX_DESIGNER_PLAN.md — preview emote-анимации
|
||
import PreviewEmoteRoute from './PreviewSkin/PreviewEmoteRoute';
|
||
|
||
function PlayerRoute() {
|
||
const { id } = useParams();
|
||
const { isAuthenticated, isLoading, error } = useAuth();
|
||
|
||
if (isLoading) return <LoadingScreen text="Подключение" />;
|
||
|
||
if (error === 'no_jwt_local') {
|
||
return (
|
||
<LoadingScreen
|
||
text="Нужен JWT"
|
||
subText={`Это dev-fallback (только localhost). На проде сразу редирект на rublox.pro. (gameId=${id})`}
|
||
devJwt
|
||
/>
|
||
);
|
||
}
|
||
|
||
if (!isAuthenticated) {
|
||
return (
|
||
<LoadingScreen
|
||
text="Сессия истекла"
|
||
subText="Вернись на rublox.pro и попробуй ещё раз."
|
||
/>
|
||
);
|
||
}
|
||
|
||
return <KubikonPlayer />;
|
||
}
|
||
|
||
function IndexRoute() {
|
||
// Прямой заход на `/` без gameId.
|
||
// На реальном деплое юзер должен прийти с ticket'ом — редиректим обратно
|
||
// на главный сайт. На dev (localhost) показываем подсказку.
|
||
if (typeof window !== 'undefined' && window.location.hostname !== 'localhost'
|
||
&& window.location.hostname !== '127.0.0.1') {
|
||
window.location.assign(RUBLOX_HOME);
|
||
return null;
|
||
}
|
||
return <LoadingScreen text="Плеер Рублокса" subText="Открой /<gameId> чтобы запустить игру." />;
|
||
}
|
||
|
||
export default function App() {
|
||
return (
|
||
<PlayerAuthProvider>
|
||
<BrowserRouter>
|
||
<Routes>
|
||
<Route path="/" element={<IndexRoute />} />
|
||
{/* Preview-режим для дизайнеров (Подфаза 3.4) — должен быть ДО /:id,
|
||
иначе React Router примет '_preview-skin' за gameId. */}
|
||
<Route path="/_preview-skin/:itemId" element={<PreviewSkinRoute />} />
|
||
{/* Preview модели (Фаза 5.6 DESIGNER_PLAN) — пустой мир с моделью */}
|
||
<Route path="/_preview-model/:itemId" element={<PreviewModelRoute />} />
|
||
{/* Preview аватара — пустой мир + персонаж с этим телом */}
|
||
<Route path="/_preview-avatar/:itemId" element={<PreviewAvatarRoute />} />
|
||
{/* Preview emote — пустой мир + бекон с проигрыванием анимации */}
|
||
<Route path="/_preview-emote/:itemId" element={<PreviewEmoteRoute />} />
|
||
<Route path="/:id" element={<PlayerRoute />} />
|
||
<Route path="*" element={<LoadingScreen text="Игра не найдена" />} />
|
||
</Routes>
|
||
</BrowserRouter>
|
||
</PlayerAuthProvider>
|
||
);
|
||
}
|