Compare commits
No commits in common. "main" and "feat/jump-3-phase" have entirely different histories.
main
...
feat/jump-
@ -1,7 +1,6 @@
|
|||||||
import React, { useEffect, useState, useRef, useCallback } from 'react';
|
import React, { useEffect, useState, useRef, useCallback } from 'react';
|
||||||
import Icon from '../editor-shared/Icon';
|
import Icon from '../editor-shared/Icon';
|
||||||
import { STORYS_addres, USER_addres } from '../api/API';
|
import { STORYS_addres, USER_addres } from '../api/API';
|
||||||
import { MIXAMO_SKINS } from '../engine/PlayerController';
|
|
||||||
|
|
||||||
const getToken = () => {
|
const getToken = () => {
|
||||||
try {
|
try {
|
||||||
@ -44,10 +43,6 @@ const HUD = {
|
|||||||
font: '"Inter", system-ui, -apple-system, sans-serif',
|
font: '"Inter", system-ui, -apple-system, sans-serif',
|
||||||
};
|
};
|
||||||
|
|
||||||
// В десктоп-приложении (Electron) пункт «Полноэкранный режим» не нужен —
|
|
||||||
// окно и так на весь экран. preload выставляет window.__RUBLOX_DESKTOP__.
|
|
||||||
const IS_DESKTOP_APP = typeof window !== 'undefined' && !!window.__RUBLOX_DESKTOP__;
|
|
||||||
|
|
||||||
const TABS = [
|
const TABS = [
|
||||||
{ id: 'people', icon: 'users', title: 'Участники' },
|
{ id: 'people', icon: 'users', title: 'Участники' },
|
||||||
{ id: 'settings', icon: 'settings', title: 'Настройки' },
|
{ id: 'settings', icon: 'settings', title: 'Настройки' },
|
||||||
@ -613,18 +608,19 @@ function PlayerCard({ player, isMe, isFriend, isPending, onAddFriend }) {
|
|||||||
let avatarUrl = null;
|
let avatarUrl = null;
|
||||||
let isSkin = false;
|
let isSkin = false;
|
||||||
if (player.skin && typeof player.skin === 'string') {
|
if (player.skin && typeof player.skin === 'string') {
|
||||||
// ВАЛИДАЦИЯ: legacy R15-скины (bacon-hair и пр.) больше не существуют.
|
const isLegacy = LEGACY_SKINS.has(player.skin);
|
||||||
// Если скин НЕ в наборе валидных Mixamo (80 шт) — показываем аватар
|
if (!isLegacy && player.skin.startsWith('skin_')) {
|
||||||
// дефолтного skin_y-bot (как и в самой игре 3D-модель валидируется).
|
// Mixamo: PNG на rublox-site (на проде rublox.pro,
|
||||||
let skinId = player.skin;
|
// локально localhost:3000) рядом с GLB.
|
||||||
if (!MIXAMO_SKINS.has(skinId) && !skinId.startsWith('customskin:')) {
|
const base = (typeof window !== 'undefined'
|
||||||
skinId = 'skin_y-bot';
|
&& window.location.hostname === 'localhost')
|
||||||
|
? 'http://localhost:3000'
|
||||||
|
: 'https://rublox.pro';
|
||||||
|
avatarUrl = `${base}/character-assets/skins/${player.skin}.png?v=20260614`;
|
||||||
|
} else {
|
||||||
|
// Legacy R15: путь по старому шаблону.
|
||||||
|
avatarUrl = `/kubikon-assets/characters/${player.skin}/avatar.png?v=2026_05_27`;
|
||||||
}
|
}
|
||||||
const base = (typeof window !== 'undefined'
|
|
||||||
&& window.location.hostname === 'localhost')
|
|
||||||
? 'http://localhost:3000'
|
|
||||||
: 'https://rublox.pro';
|
|
||||||
avatarUrl = `${base}/character-assets/skins/${skinId}.png?v=20260614`;
|
|
||||||
isSkin = true;
|
isSkin = true;
|
||||||
} else if (player.photo_thumb_b64) {
|
} else if (player.photo_thumb_b64) {
|
||||||
avatarUrl = player.photo_thumb_b64.startsWith('data:')
|
avatarUrl = player.photo_thumb_b64.startsWith('data:')
|
||||||
@ -935,7 +931,6 @@ function TabSettings({ sceneRef }) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<SettingsSection title="Экран" />
|
<SettingsSection title="Экран" />
|
||||||
{!IS_DESKTOP_APP && (
|
|
||||||
<ArrowsRow
|
<ArrowsRow
|
||||||
label="Полноэкранный режим"
|
label="Полноэкранный режим"
|
||||||
hint="Развернуть игру на весь экран"
|
hint="Развернуть игру на весь экран"
|
||||||
@ -952,7 +947,6 @@ function TabSettings({ sceneRef }) {
|
|||||||
} catch (e) { /* ignore */ }
|
} catch (e) { /* ignore */ }
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
<SliderRow
|
<SliderRow
|
||||||
label="Качество графики"
|
label="Качество графики"
|
||||||
hint="Разрешение рендера и тени"
|
hint="Разрешение рендера и тени"
|
||||||
|
|||||||
@ -25,24 +25,6 @@ import useDeviceType from '../hooks/useDeviceType';
|
|||||||
import KubikonMobileControls from './KubikonMobileControls';
|
import KubikonMobileControls from './KubikonMobileControls';
|
||||||
import GameLoadingScreen from './GameLoadingScreen';
|
import GameLoadingScreen from './GameLoadingScreen';
|
||||||
|
|
||||||
// В десктоп-приложении (Electron-обёртка rublox-desktop) окно уже на весь
|
|
||||||
// экран без браузерной панели и без вкладок — fullscreen не нужен (раньше он
|
|
||||||
// защищал от случайного Ctrl+W/Ctrl+T в браузере; в Electron этого риска нет).
|
|
||||||
// preload выставляет window.__RUBLOX_DESKTOP__.
|
|
||||||
const IS_DESKTOP_APP = typeof window !== 'undefined' && !!window.__RUBLOX_DESKTOP__;
|
|
||||||
|
|
||||||
// В Android-приложении (Capacitor-обёртка rublox-android) WebView уже на весь
|
|
||||||
// экран — браузерный fullscreen не нужен, а стартовый оверлей «Нажми чтобы
|
|
||||||
// играть» избыточен (в браузере он нужен для user-gesture перед FS, в APK
|
|
||||||
// этого барьера не требуется). Capacitor выставляет window.Capacitor.
|
|
||||||
const IS_ANDROID_APP = typeof window !== 'undefined'
|
|
||||||
&& (!!window.Capacitor
|
|
||||||
|| /RubloxAndroid/i.test(navigator.userAgent || ''));
|
|
||||||
|
|
||||||
// Объединённый признак «нативного приложения» (десктоп ИЛИ Android) — там,
|
|
||||||
// где поведение совпадает (не дёргать fullscreen).
|
|
||||||
const IS_NATIVE_APP = IS_DESKTOP_APP || IS_ANDROID_APP;
|
|
||||||
|
|
||||||
// Плеер живёт на player.rublox.pro — он не знает SPA-роутов Майнкрафтии
|
// Плеер живёт на player.rublox.pro — он не знает SPA-роутов Майнкрафтии
|
||||||
// (/kubikon, /login, /auth). Поэтому вместо navigate(...) делаем
|
// (/kubikon, /login, /auth). Поэтому вместо navigate(...) делаем
|
||||||
// явный window.location.assign на внешний домен.
|
// явный window.location.assign на внешний домен.
|
||||||
@ -248,10 +230,7 @@ const KubikonPlayer = () => {
|
|||||||
// ВКЛЮЧИТЬ fullscreen и заблокировать Ctrl+W/Ctrl+T и др. системные
|
// ВКЛЮЧИТЬ fullscreen и заблокировать Ctrl+W/Ctrl+T и др. системные
|
||||||
// хоткеи. Без этого браузер закрывает вкладку при случайном Ctrl+W.
|
// хоткеи. Без этого браузер закрывает вкладку при случайном Ctrl+W.
|
||||||
// requestFullscreen() требует user gesture — поэтому без клика никак.
|
// requestFullscreen() требует user gesture — поэтому без клика никак.
|
||||||
// В нативном приложении (Electron/Capacitor) fullscreen не нужен (окно и
|
const [gameStarted, setGameStarted] = useState(false);
|
||||||
// так на весь экран), поэтому стартовый оверлей пропускаем — игра
|
|
||||||
// запускается сразу.
|
|
||||||
const [gameStarted, setGameStarted] = useState(IS_NATIVE_APP);
|
|
||||||
const [hp, setHp] = useState({ hp: 100, maxHp: 100 });
|
const [hp, setHp] = useState({ hp: 100, maxHp: 100 });
|
||||||
// Скрипт через game.hud.setVisible(false) полностью скрывает стандартный HUD.
|
// Скрипт через game.hud.setVisible(false) полностью скрывает стандартный HUD.
|
||||||
const [stdHudVisible, setStdHudVisible] = useState(true);
|
const [stdHudVisible, setStdHudVisible] = useState(true);
|
||||||
@ -1145,24 +1124,21 @@ const KubikonPlayer = () => {
|
|||||||
|| root.webkitRequestFullscreen
|
|| root.webkitRequestFullscreen
|
||||||
|| root.mozRequestFullScreen
|
|| root.mozRequestFullScreen
|
||||||
|| root.msRequestFullscreen;
|
|| root.msRequestFullscreen;
|
||||||
// В нативном приложении (Electron/Capacitor) окно и так на весь
|
if (req) {
|
||||||
// экран — FS не нужен.
|
|
||||||
if (req && !IS_NATIVE_APP) {
|
|
||||||
try { await req.call(root); } catch (e) { /* отменено */ }
|
try { await req.call(root); } catch (e) { /* отменено */ }
|
||||||
}
|
}
|
||||||
setMobileStartTapped(true);
|
setMobileStartTapped(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
/** Стартовый клик «Начать игру» — запрашивает fullscreen
|
/** Стартовый клик «Начать игру» — запрашивает fullscreen
|
||||||
* (Chrome блокирует Ctrl+W/Ctrl+T в fullscreen) и снимает оверлей.
|
* (Chrome блокирует Ctrl+W/Ctrl+T в fullscreen) и снимает оверлей. */
|
||||||
* В нативном приложении (Electron/Capacitor) FS не нужен. */
|
|
||||||
const handleGameStart = useCallback(async () => {
|
const handleGameStart = useCallback(async () => {
|
||||||
const root = document.documentElement;
|
const root = document.documentElement;
|
||||||
const req = root.requestFullscreen
|
const req = root.requestFullscreen
|
||||||
|| root.webkitRequestFullscreen
|
|| root.webkitRequestFullscreen
|
||||||
|| root.mozRequestFullScreen
|
|| root.mozRequestFullScreen
|
||||||
|| root.msRequestFullscreen;
|
|| root.msRequestFullscreen;
|
||||||
if (req && !IS_NATIVE_APP) {
|
if (req) {
|
||||||
try { await req.call(root); } catch (e) { /* юзер запретил — играем без FS */ }
|
try { await req.call(root); } catch (e) { /* юзер запретил — играем без FS */ }
|
||||||
}
|
}
|
||||||
setGameStarted(true);
|
setGameStarted(true);
|
||||||
@ -1310,18 +1286,11 @@ const KubikonPlayer = () => {
|
|||||||
lineHeight: 1.4,
|
lineHeight: 1.4,
|
||||||
padding: '0 24px',
|
padding: '0 24px',
|
||||||
}}>
|
}}>
|
||||||
{IS_DESKTOP_APP ? (
|
Игра откроется в полноэкранном режиме —
|
||||||
<>Управление: <b>WASD</b> — движение, <b>пробел</b> — прыжок,
|
это защитит от случайного закрытия вкладки
|
||||||
мышь — камера.</>
|
(Ctrl+W, Ctrl+T и др.).
|
||||||
) : (
|
<br />
|
||||||
<>
|
Выход: <b>Esc</b> или <b>F11</b>.
|
||||||
Игра откроется в полноэкранном режиме —
|
|
||||||
это защитит от случайного закрытия вкладки
|
|
||||||
(Ctrl+W, Ctrl+T и др.).
|
|
||||||
<br />
|
|
||||||
Выход: <b>Esc</b> или <b>F11</b>.
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user