diff --git a/src/editor/KubikonEditor.jsx b/src/editor/KubikonEditor.jsx index 2d1cacc..f234316 100644 --- a/src/editor/KubikonEditor.jsx +++ b/src/editor/KubikonEditor.jsx @@ -4,6 +4,7 @@ import { jwtDecode } from 'jwt-decode'; import { useAuth, redirectToLogin } from '../auth/AuthContext.jsx'; import { useSanctions } from '../auth/SanctionsContext.jsx'; import { BabylonScene } from './engine/BabylonScene'; +import { MIXAMO_SKINS } from './engine/PlayerController'; import { StudioCollab } from './engine/StudioCollab'; import { CollabOverlay } from './engine/CollabOverlay'; import { BLOCK_TYPES, BLOCK_CATEGORIES, blockPreview, registerCustomBlockType } from './engine/BlockTypes'; @@ -938,7 +939,7 @@ const KubikonEditor = () => { // === Game settings inline в TopRibbon (вкладка Тест) === // Дефолт — R15-скин bacon-hair (классический Roblox-вид). - const [playerModelType, setPlayerModelTypeUI] = useState('skin_bacon-hair'); + const [playerModelType, setPlayerModelTypeUI] = useState('skin_y-bot'); const [envPreset, setEnvPresetUI] = useState('day'); const [dayDurationMin, setDayDurationMinUI] = useState(5); const [nightDurationMin, setNightDurationMinUI] = useState(3); @@ -964,7 +965,7 @@ const KubikonEditor = () => { genre: 'other', thumbnail: '', is_public: false, - player_model_type: 'skin_bacon-hair', + player_model_type: 'skin_y-bot', }); const projectNameRef = useRef(projectName); useEffect(() => { projectNameRef.current = projectName; metaRef.current.title = projectName; }, [projectName]); @@ -1782,7 +1783,7 @@ const KubikonEditor = () => { sceneRef.current.history?.initialize(); // Синхронизируем UI-state TopRibbon из загруженной сцены try { - setPlayerModelTypeUI(sceneRef.current.getPlayerModelType?.() || 'skin_bacon-hair'); + setPlayerModelTypeUI(sceneRef.current.getPlayerModelType?.() || 'skin_y-bot'); const env = sceneRef.current.getEnvironmentState?.(); if (env?.preset) setEnvPresetUI(env.preset); if (env?.dayDurationMin) setDayDurationMinUI(env.dayDurationMin); @@ -2093,7 +2094,16 @@ const KubikonEditor = () => { const uid = getCurrentUserId(); if (uid) { const r = await Kubikon3DApi.getEquippedSkin(uid); - const sf = r?.data?.skin_folder; + let sf = r?.data?.skin_folder; + // ВАЛИДАЦИЯ: legacy R15-скины (bacon-hair и пр.) больше + // не существуют. Если БД отдала невалидный — подменяем + // на skin_y-bot (как в плеере и кабинете). + if (sf && typeof sf === 'string' + && !MIXAMO_SKINS.has(sf) + && !sf.startsWith('customskin:')) { + console.log('[KubikonEditor] skin', sf, 'не валиден → skin_y-bot'); + sf = 'skin_y-bot'; + } if (sf && typeof sf === 'string') { // Подмешиваем в hash так чтобы не сломать ticket=... const cur = window.location.hash || ''; diff --git a/src/editor/engine/PlayerController.js b/src/editor/engine/PlayerController.js index 682ff86..77fa4b9 100644 --- a/src/editor/engine/PlayerController.js +++ b/src/editor/engine/PlayerController.js @@ -32,7 +32,7 @@ import { MixamoAnimator, loadMixamoAnimations } from './MixamoAnimator'; // Список всех Mixamo-скинов. Должен совпадать со списком в плеере и // каталоге сайта (rublox-site/src/data/skinsCatalog.js). -const MIXAMO_SKINS = new Set([ +export const MIXAMO_SKINS = new Set([ 'skin_aj', 'skin_akai', 'skin_arissa', 'skin_big-vegas', 'skin_castle-guard-1', 'skin_castle-guard-2', 'skin_ch01', 'skin_ch02', 'skin_ch03', 'skin_ch04', 'skin_ch07', 'skin_ch08',