add: VersionExplorer, don't work first run Minecraft
This commit is contained in:
@ -804,6 +804,53 @@ export function initMinecraftHandlers() {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
|
||||
// Добавьте в функцию initMinecraftHandlers новый обработчик
|
||||
ipcMain.handle('get-available-versions', async (event) => {
|
||||
try {
|
||||
const appPath = path.dirname(app.getPath('exe'));
|
||||
const minecraftDir = path.join(appPath, '.minecraft');
|
||||
const versionsDir = path.join(minecraftDir, 'versions');
|
||||
|
||||
if (!fs.existsSync(versionsDir)) {
|
||||
return { success: true, versions: [] };
|
||||
}
|
||||
|
||||
const items = fs.readdirSync(versionsDir);
|
||||
const versions = [];
|
||||
|
||||
for (const item of items) {
|
||||
const versionPath = path.join(versionsDir, item);
|
||||
if (fs.statSync(versionPath).isDirectory()) {
|
||||
// Проверяем, есть ли конфигурация для пакета
|
||||
const versionJsonPath = path.join(versionPath, `${item}.json`);
|
||||
let versionInfo = {
|
||||
id: item,
|
||||
name: item,
|
||||
version: item,
|
||||
};
|
||||
|
||||
if (fs.existsSync(versionJsonPath)) {
|
||||
try {
|
||||
const versionData = JSON.parse(
|
||||
fs.readFileSync(versionJsonPath, 'utf8'),
|
||||
);
|
||||
versionInfo.version = versionData.id || item;
|
||||
} catch (error) {
|
||||
console.warn(`Ошибка при чтении файла версии ${item}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
versions.push(versionInfo);
|
||||
}
|
||||
}
|
||||
|
||||
return { success: true, versions };
|
||||
} catch (error) {
|
||||
console.error('Ошибка при получении доступных версий:', error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Добавляем обработчики IPC для аутентификации
|
||||
@ -979,3 +1026,57 @@ export function initPackConfigHandlers() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Добавляем после обработчика get-available-versions
|
||||
ipcMain.handle('get-version-config', async (event, { versionId }) => {
|
||||
try {
|
||||
const appPath = path.dirname(app.getPath('exe'));
|
||||
const minecraftDir = path.join(appPath, '.minecraft');
|
||||
const versionsDir = path.join(minecraftDir, 'versions');
|
||||
const versionPath = path.join(versionsDir, versionId);
|
||||
|
||||
// Проверяем существование директории версии
|
||||
if (!fs.existsSync(versionPath)) {
|
||||
return { success: false, error: `Версия ${versionId} не найдена` };
|
||||
}
|
||||
|
||||
// Проверяем конфигурационный файл версии
|
||||
const configPath = path.join(versionPath, 'popa-launcher-config.json');
|
||||
|
||||
// Определяем базовые настройки по умолчанию
|
||||
let config = {
|
||||
downloadUrl: '',
|
||||
apiReleaseUrl: '',
|
||||
versionFileName: `${versionId}_version.txt`,
|
||||
packName: versionId,
|
||||
memory: 4096,
|
||||
baseVersion: '1.21.4',
|
||||
serverIp: 'popa-popa.ru',
|
||||
fabricVersion: '0.16.14',
|
||||
preserveFiles: ['popa-launcher-config.json'],
|
||||
};
|
||||
|
||||
// Если это Comfort, используем настройки по умолчанию
|
||||
if (versionId === 'Comfort') {
|
||||
config.downloadUrl =
|
||||
'https://github.com/DIKER0K/Comfort/releases/latest/download/Comfort.zip';
|
||||
config.apiReleaseUrl =
|
||||
'https://api.github.com/repos/DIKER0K/Comfort/releases/latest';
|
||||
}
|
||||
|
||||
// Если есть конфигурационный файл, загружаем из него
|
||||
if (fs.existsSync(configPath)) {
|
||||
try {
|
||||
const savedConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||
config = { ...config, ...savedConfig };
|
||||
} catch (error) {
|
||||
console.warn(`Ошибка чтения конфигурации ${versionId}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
return { success: true, config };
|
||||
} catch (error) {
|
||||
console.error('Ошибка получения настроек версии:', error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
|
@ -3,6 +3,7 @@ import {
|
||||
Routes,
|
||||
Route,
|
||||
Navigate,
|
||||
useNavigate,
|
||||
} from 'react-router-dom';
|
||||
import Login from './pages/Login';
|
||||
import LaunchPage from './pages/LaunchPage';
|
||||
@ -12,19 +13,7 @@ import TopBar from './components/TopBar';
|
||||
import { Box } from '@mui/material';
|
||||
import MinecraftBackround from './components/MinecraftBackround';
|
||||
import { Notifier } from './components/Notifier';
|
||||
|
||||
// Переместите launchOptions сюда, вне компонентов
|
||||
const launchOptions = {
|
||||
downloadUrl:
|
||||
'https://github.com/DIKER0K/Comfort/releases/latest/download/Comfort.zip',
|
||||
apiReleaseUrl: 'https://api.github.com/repos/DIKER0K/Comfort/releases/latest',
|
||||
versionFileName: 'comfort_version.txt',
|
||||
packName: 'Comfort',
|
||||
memory: 4096,
|
||||
baseVersion: '1.21.4',
|
||||
serverIp: 'popa-popa.ru',
|
||||
fabricVersion: '0.16.14', // Уберите префикс "fabric"
|
||||
};
|
||||
import { VersionsExplorer } from './pages/VersionsExplorer';
|
||||
|
||||
const AuthCheck = ({ children }: { children: ReactNode }) => {
|
||||
const [isAuthenticated, setIsAuthenticated] = useState<boolean | null>(null);
|
||||
@ -102,7 +91,15 @@ const App = () => {
|
||||
path="/"
|
||||
element={
|
||||
<AuthCheck>
|
||||
<LaunchPage launchOptions={launchOptions} />
|
||||
<VersionsExplorer />
|
||||
</AuthCheck>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/launch/:versionId"
|
||||
element={
|
||||
<AuthCheck>
|
||||
<LaunchPage />
|
||||
</AuthCheck>
|
||||
}
|
||||
/>
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { Box, Button, Typography } from '@mui/material';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import MinimizeIcon from '@mui/icons-material/Minimize';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
@ -24,6 +25,12 @@ export default function TopBar({ onRegister }: TopBarProps) {
|
||||
// Получаем текущий путь
|
||||
const location = useLocation();
|
||||
const isLoginPage = location.pathname === '/login';
|
||||
const isLaunchPage = location.pathname.startsWith('/launch');
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleLaunchPage = () => {
|
||||
navigate('/');
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
@ -38,9 +45,37 @@ export default function TopBar({ onRegister }: TopBarProps) {
|
||||
width: '100%',
|
||||
WebkitAppRegion: 'drag',
|
||||
overflow: 'hidden',
|
||||
justifyContent: 'flex-end', // Всё содержимое справа
|
||||
justifyContent: 'space-between', // Всё содержимое справа
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
WebkitAppRegion: 'no-drag',
|
||||
gap: '2vw',
|
||||
padding: '1em',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
{isLaunchPage && (
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
onClick={() => handleLaunchPage()}
|
||||
sx={{
|
||||
width: '3em',
|
||||
height: '3em',
|
||||
borderRadius: '50%',
|
||||
border: 'unset',
|
||||
color: 'white',
|
||||
minWidth: 'unset',
|
||||
minHeight: 'unset',
|
||||
}}
|
||||
>
|
||||
<ArrowBackIcon />
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
{/* Правая часть со всеми кнопками */}
|
||||
<Box
|
||||
sx={{
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
Modal,
|
||||
} from '@mui/material';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import ServerStatus from '../components/ServerStatus/ServerStatus';
|
||||
import PopaPopa from '../components/popa-popa';
|
||||
import SettingsIcon from '@mui/icons-material/Settings';
|
||||
@ -30,7 +30,9 @@ declare global {
|
||||
|
||||
// Определяем тип для props
|
||||
interface LaunchPageProps {
|
||||
launchOptions: {
|
||||
onLaunchPage?: () => void;
|
||||
launchOptions?: {
|
||||
// Делаем опциональным
|
||||
downloadUrl: string;
|
||||
apiReleaseUrl: string;
|
||||
versionFileName: string;
|
||||
@ -42,8 +44,11 @@ interface LaunchPageProps {
|
||||
};
|
||||
}
|
||||
|
||||
const LaunchPage = ({ launchOptions }: LaunchPageProps) => {
|
||||
const LaunchPage = ({ onLaunchPage, launchOptions = {} as any }: LaunchPageProps) => {
|
||||
const navigate = useNavigate();
|
||||
const { versionId } = useParams();
|
||||
const [versionConfig, setVersionConfig] = useState<any>(null);
|
||||
|
||||
// Начальное состояние должно быть пустым или с минимальными значениями
|
||||
const [config, setConfig] = useState<{
|
||||
memory: number;
|
||||
@ -101,27 +106,82 @@ const LaunchPage = ({ launchOptions }: LaunchPageProps) => {
|
||||
}, [navigate]);
|
||||
|
||||
useEffect(() => {
|
||||
// Загрузка конфигурации сборки при монтировании
|
||||
const loadPackConfig = async () => {
|
||||
const fetchVersionConfig = async () => {
|
||||
if (!versionId) return;
|
||||
|
||||
try {
|
||||
// Сначала проверяем, есть ли конфигурация в localStorage
|
||||
const savedConfig = localStorage.getItem('selected_version_config');
|
||||
if (savedConfig) {
|
||||
const parsedConfig = JSON.parse(savedConfig);
|
||||
setVersionConfig(parsedConfig);
|
||||
|
||||
// Устанавливаем значения памяти и preserveFiles из конфигурации
|
||||
setConfig({
|
||||
memory: parsedConfig.memory || 4096,
|
||||
preserveFiles: parsedConfig.preserveFiles || [],
|
||||
});
|
||||
|
||||
// Очищаем localStorage
|
||||
localStorage.removeItem('selected_version_config');
|
||||
return;
|
||||
}
|
||||
|
||||
// Если нет в localStorage, запрашиваем с сервера
|
||||
const result = await window.electron.ipcRenderer.invoke(
|
||||
'load-pack-config',
|
||||
{
|
||||
packName: launchOptions.packName,
|
||||
},
|
||||
'get-version-config',
|
||||
{ versionId },
|
||||
);
|
||||
|
||||
if (result.success && result.config) {
|
||||
// Полностью заменяем config значениями из файла
|
||||
setConfig(result.config);
|
||||
if (result.success) {
|
||||
setVersionConfig(result.config);
|
||||
setConfig({
|
||||
memory: result.config.memory || 4096,
|
||||
preserveFiles: result.config.preserveFiles || [],
|
||||
});
|
||||
} else {
|
||||
// Если не удалось получить конфигурацию, используем значения по умолчанию
|
||||
const defaultConfig = {
|
||||
downloadUrl: '',
|
||||
apiReleaseUrl: '',
|
||||
versionFileName: `${versionId}_version.txt`,
|
||||
packName: versionId || 'Comfort',
|
||||
memory: 4096,
|
||||
baseVersion: '1.21.4',
|
||||
serverIp: 'popa-popa.ru',
|
||||
fabricVersion: '0.16.14',
|
||||
preserveFiles: ['popa-launcher-config.json'],
|
||||
};
|
||||
setVersionConfig(defaultConfig);
|
||||
setConfig({
|
||||
memory: defaultConfig.memory,
|
||||
preserveFiles: defaultConfig.preserveFiles || [],
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка при загрузке настроек:', error);
|
||||
console.error('Ошибка при получении настроек версии:', error);
|
||||
// Используем значения по умолчанию
|
||||
const defaultConfig = {
|
||||
downloadUrl: '',
|
||||
apiReleaseUrl: '',
|
||||
versionFileName: `${versionId}_version.txt`,
|
||||
packName: versionId || 'Comfort',
|
||||
memory: 4096,
|
||||
baseVersion: '1.21.4',
|
||||
serverIp: 'popa-popa.ru',
|
||||
fabricVersion: '0.16.14',
|
||||
preserveFiles: ['popa-launcher-config.json'],
|
||||
};
|
||||
setVersionConfig(defaultConfig);
|
||||
setConfig({
|
||||
memory: defaultConfig.memory,
|
||||
preserveFiles: defaultConfig.preserveFiles || [],
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
loadPackConfig();
|
||||
}, [launchOptions.packName]);
|
||||
fetchVersionConfig();
|
||||
}, [versionId]);
|
||||
|
||||
const showNotification = (
|
||||
message: string,
|
||||
@ -134,94 +194,86 @@ const LaunchPage = ({ launchOptions }: LaunchPageProps) => {
|
||||
setNotification({ ...notification, open: false });
|
||||
};
|
||||
|
||||
// Функция для запуска игры с настройками выбранной версии
|
||||
const handleLaunchMinecraft = async () => {
|
||||
try {
|
||||
setIsDownloading(true);
|
||||
setDownloadProgress(0);
|
||||
setBuffer(10);
|
||||
|
||||
// Загружаем настройки сборки
|
||||
const result = await window.electron.ipcRenderer.invoke(
|
||||
'load-pack-config',
|
||||
{
|
||||
packName: launchOptions.packName,
|
||||
},
|
||||
);
|
||||
// Используем настройки выбранной версии или дефолтные
|
||||
const currentConfig = versionConfig || {
|
||||
packName: versionId || 'Comfort',
|
||||
memory: 4096,
|
||||
baseVersion: '1.21.4',
|
||||
serverIp: 'popa-popa.ru',
|
||||
fabricVersion: '0.16.14',
|
||||
preserveFiles: [],
|
||||
};
|
||||
|
||||
// Используйте уже существующий state вместо локальной переменной
|
||||
if (result.success && result.config) {
|
||||
setConfig(result.config); // Обновляем state
|
||||
// Проверяем, является ли это ванильной версией
|
||||
const isVanillaVersion =
|
||||
!currentConfig.downloadUrl || currentConfig.downloadUrl === '';
|
||||
|
||||
if (!isVanillaVersion) {
|
||||
// Если это не ванильная версия, выполняем загрузку и распаковку
|
||||
const packOptions = {
|
||||
downloadUrl: currentConfig.downloadUrl,
|
||||
apiReleaseUrl: currentConfig.apiReleaseUrl,
|
||||
versionFileName: currentConfig.versionFileName,
|
||||
packName: versionId || currentConfig.packName,
|
||||
preserveFiles: config.preserveFiles,
|
||||
};
|
||||
|
||||
// Передаем опции для скачивания
|
||||
const downloadResult = await window.electron.ipcRenderer.invoke(
|
||||
'download-and-extract',
|
||||
packOptions,
|
||||
);
|
||||
|
||||
if (downloadResult?.success) {
|
||||
if (downloadResult.updated) {
|
||||
showNotification(
|
||||
`Сборка ${downloadResult.packName} успешно обновлена до версии ${downloadResult.version}`,
|
||||
'success',
|
||||
);
|
||||
} else {
|
||||
showNotification(
|
||||
`Установлена актуальная версия сборки ${downloadResult.packName} (${downloadResult.version})`,
|
||||
'info',
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
showNotification('Запускаем ванильный Minecraft...', 'info');
|
||||
}
|
||||
|
||||
// Опции для запуска Minecraft
|
||||
const savedConfig = JSON.parse(
|
||||
localStorage.getItem('launcher_config') || '{}',
|
||||
);
|
||||
|
||||
// Опции для скачивания сборки
|
||||
const packOptions = {
|
||||
downloadUrl: launchOptions.downloadUrl,
|
||||
apiReleaseUrl: launchOptions.apiReleaseUrl,
|
||||
versionFileName: launchOptions.versionFileName,
|
||||
packName: launchOptions.packName,
|
||||
preserveFiles: config.preserveFiles,
|
||||
const options = {
|
||||
accessToken: savedConfig.accessToken,
|
||||
uuid: savedConfig.uuid,
|
||||
username: savedConfig.username,
|
||||
memory: config.memory,
|
||||
baseVersion: currentConfig.baseVersion,
|
||||
packName: versionId || currentConfig.packName,
|
||||
serverIp: currentConfig.serverIp,
|
||||
fabricVersion: currentConfig.fabricVersion,
|
||||
// Для ванильной версии устанавливаем флаг
|
||||
isVanillaVersion: isVanillaVersion,
|
||||
versionToLaunchOverride: isVanillaVersion ? versionId : undefined,
|
||||
};
|
||||
|
||||
// Передаем опции для скачивания
|
||||
const downloadResult = await window.electron.ipcRenderer.invoke(
|
||||
'download-and-extract',
|
||||
packOptions,
|
||||
const launchResult = await window.electron.ipcRenderer.invoke(
|
||||
'launch-minecraft',
|
||||
options,
|
||||
);
|
||||
|
||||
if (downloadResult?.success) {
|
||||
let needsSecondAttempt = false;
|
||||
|
||||
if (downloadResult.updated) {
|
||||
showNotification(
|
||||
`Сборка ${downloadResult.packName} успешно обновлена до версии ${downloadResult.version}`,
|
||||
'success',
|
||||
);
|
||||
needsSecondAttempt = true;
|
||||
} else {
|
||||
showNotification(
|
||||
`Установлена актуальная версия сборки ${downloadResult.packName} (${downloadResult.version})`,
|
||||
'info',
|
||||
);
|
||||
}
|
||||
|
||||
// Опции для запуска
|
||||
const options = {
|
||||
accessToken: savedConfig.accessToken,
|
||||
uuid: savedConfig.uuid,
|
||||
username: savedConfig.username,
|
||||
memory: config.memory, // Используем state
|
||||
baseVersion: launchOptions.baseVersion,
|
||||
packName: launchOptions.packName,
|
||||
serverIp: launchOptions.serverIp,
|
||||
fabricVersion: launchOptions.fabricVersion,
|
||||
};
|
||||
|
||||
const launchResult = await window.electron.ipcRenderer.invoke(
|
||||
'launch-minecraft',
|
||||
options,
|
||||
);
|
||||
|
||||
if (needsSecondAttempt) {
|
||||
showNotification(
|
||||
'Завершаем настройку компонентов, повторный запуск...',
|
||||
'info',
|
||||
);
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
|
||||
const secondAttempt = await window.electron.ipcRenderer.invoke(
|
||||
'launch-minecraft',
|
||||
options,
|
||||
);
|
||||
|
||||
showNotification('Minecraft успешно запущен!', 'success');
|
||||
} else if (launchResult?.success) {
|
||||
showNotification('Minecraft успешно запущен!', 'success');
|
||||
}
|
||||
if (launchResult?.success) {
|
||||
showNotification('Minecraft успешно запущен!', 'success');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
@ -240,7 +292,7 @@ const LaunchPage = ({ launchOptions }: LaunchPageProps) => {
|
||||
};
|
||||
|
||||
await window.electron.ipcRenderer.invoke('save-pack-config', {
|
||||
packName: launchOptions.packName,
|
||||
packName: versionId || versionConfig?.packName || 'Comfort',
|
||||
config: configToSave,
|
||||
});
|
||||
|
||||
@ -285,7 +337,7 @@ const LaunchPage = ({ launchOptions }: LaunchPageProps) => {
|
||||
|
||||
<Box>
|
||||
<ServerStatus
|
||||
serverIp={launchOptions.serverIp}
|
||||
serverIp={versionConfig?.serverIp || 'popa-popa.ru'}
|
||||
refreshInterval={30000}
|
||||
/>
|
||||
</Box>
|
||||
@ -393,8 +445,8 @@ const LaunchPage = ({ launchOptions }: LaunchPageProps) => {
|
||||
Файлы и папки, которые будут сохранены после переустановки сборки
|
||||
</Typography>
|
||||
<FilesSelector
|
||||
packName={launchOptions.packName}
|
||||
initialSelected={config.preserveFiles} // Передаем текущие выбранные файлы
|
||||
packName={versionId || versionConfig?.packName || 'Comfort'}
|
||||
initialSelected={config.preserveFiles}
|
||||
onSelectionChange={(selected) => {
|
||||
setConfig((prev) => ({ ...prev, preserveFiles: selected }));
|
||||
}}
|
||||
|
268
src/renderer/pages/VersionsExplorer.tsx
Normal file
268
src/renderer/pages/VersionsExplorer.tsx
Normal file
@ -0,0 +1,268 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
Grid,
|
||||
Card,
|
||||
CardMedia,
|
||||
CardContent,
|
||||
CardActions,
|
||||
Button,
|
||||
CircularProgress,
|
||||
} from '@mui/material';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
interface VersionCardProps {
|
||||
id: string;
|
||||
name: string;
|
||||
imageUrl: string;
|
||||
version: string;
|
||||
onSelect: (id: string) => void;
|
||||
}
|
||||
|
||||
const VersionCard: React.FC<VersionCardProps> = ({
|
||||
id,
|
||||
name,
|
||||
imageUrl,
|
||||
version,
|
||||
onSelect,
|
||||
}) => {
|
||||
return (
|
||||
<Card
|
||||
sx={{
|
||||
backgroundColor: 'rgba(30, 30, 50, 0.8)',
|
||||
backdropFilter: 'blur(10px)',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
borderRadius: '16px',
|
||||
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.3)',
|
||||
transition: 'transform 0.3s, box-shadow 0.3s',
|
||||
overflow: 'hidden',
|
||||
'&:hover': {
|
||||
transform: 'translateY(-8px)',
|
||||
boxShadow: '0 12px 40px rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box sx={{ position: 'relative', overflow: 'hidden', width: '100%' }}>
|
||||
<CardMedia
|
||||
component="img"
|
||||
height="180"
|
||||
image={'https://placehold.co/300x140?text=' + name}
|
||||
alt={name}
|
||||
sx={{
|
||||
transition: 'transform 0.5s',
|
||||
'&:hover': {
|
||||
transform: 'scale(1.05)',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: 0,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.6)',
|
||||
color: '#fff',
|
||||
padding: '4px 12px',
|
||||
borderBottomLeftRadius: '12px',
|
||||
}}
|
||||
>
|
||||
<Typography variant="caption" fontWeight="bold">
|
||||
{version}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<CardContent sx={{ flexGrow: 1, padding: '16px' }}>
|
||||
<Typography
|
||||
gutterBottom
|
||||
variant="h5"
|
||||
component="div"
|
||||
sx={{
|
||||
fontWeight: 'bold',
|
||||
color: '#ffffff',
|
||||
fontSize: '1.5rem',
|
||||
}}
|
||||
>
|
||||
{name}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
sx={{
|
||||
color: 'rgba(255, 255, 255, 0.7)',
|
||||
marginBottom: '12px',
|
||||
}}
|
||||
>
|
||||
Версия: {version}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
|
||||
<CardActions sx={{ padding: '0 16px 16px' }}>
|
||||
<Button
|
||||
fullWidth
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => onSelect(id)}
|
||||
sx={{
|
||||
borderRadius: '12px',
|
||||
textTransform: 'none',
|
||||
fontWeight: 'bold',
|
||||
padding: '10px 0',
|
||||
background: 'linear-gradient(90deg, #3a7bd5, #6d5bf1)',
|
||||
'&:hover': {
|
||||
background: 'linear-gradient(90deg, #4a8be5, #7d6bf1)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
Играть
|
||||
</Button>
|
||||
</CardActions>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
interface VersionInfo {
|
||||
id: string;
|
||||
name: string;
|
||||
version: string;
|
||||
imageUrl?: string;
|
||||
config?: {
|
||||
downloadUrl: string;
|
||||
apiReleaseUrl: string;
|
||||
versionFileName: string;
|
||||
packName: string;
|
||||
memory: number;
|
||||
baseVersion: string;
|
||||
serverIp: string;
|
||||
fabricVersion: string;
|
||||
preserveFiles: string[];
|
||||
};
|
||||
}
|
||||
|
||||
// В компоненте VersionsExplorer
|
||||
export const VersionsExplorer = () => {
|
||||
const [versions, setVersions] = useState<VersionInfo[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
const fetchVersions = async () => {
|
||||
try {
|
||||
const result = await window.electron.ipcRenderer.invoke(
|
||||
'get-available-versions',
|
||||
);
|
||||
if (result.success) {
|
||||
// Для каждой версии получаем её конфигурацию
|
||||
const versionsWithConfig = await Promise.all(
|
||||
result.versions.map(async (version: VersionInfo) => {
|
||||
const configResult = await window.electron.ipcRenderer.invoke(
|
||||
'get-version-config',
|
||||
{ versionId: version.id },
|
||||
);
|
||||
|
||||
return {
|
||||
...version,
|
||||
config: configResult.success ? configResult.config : undefined,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
setVersions(versionsWithConfig);
|
||||
} else {
|
||||
console.error('Ошибка получения версий:', result.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка при запросе версий:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchVersions();
|
||||
}, []);
|
||||
|
||||
const handleSelectVersion = (version: VersionInfo) => {
|
||||
// Сохраняем конфигурацию в localStorage для использования в LaunchPage
|
||||
localStorage.setItem(
|
||||
'selected_version_config',
|
||||
JSON.stringify(version.config || {}),
|
||||
);
|
||||
navigate(`/launch/${version.id}`);
|
||||
};
|
||||
|
||||
// Тестовая версия, если нет доступных
|
||||
const displayVersions =
|
||||
versions.length > 0
|
||||
? versions
|
||||
: [
|
||||
{
|
||||
id: 'Comfort',
|
||||
name: 'Comfort',
|
||||
version: '1.21.4-fabric0.16.14',
|
||||
imageUrl: 'https://via.placeholder.com/300x140?text=Comfort',
|
||||
config: {
|
||||
downloadUrl:
|
||||
'https://github.com/DIKER0K/Comfort/releases/latest/download/Comfort.zip',
|
||||
apiReleaseUrl:
|
||||
'https://api.github.com/repos/DIKER0K/Comfort/releases/latest',
|
||||
versionFileName: 'comfort_version.txt',
|
||||
packName: 'Comfort',
|
||||
memory: 4096,
|
||||
baseVersion: '1.21.4',
|
||||
serverIp: 'popa-popa.ru',
|
||||
fabricVersion: '0.16.14',
|
||||
preserveFiles: ['popa-launcher-config.json'],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: '100vw',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography variant="h4" sx={{ mb: 4 }}>
|
||||
Доступные версии
|
||||
</Typography>
|
||||
|
||||
{loading ? (
|
||||
<Box display="flex" justifyContent="center" my={5}>
|
||||
<CircularProgress />
|
||||
</Box>
|
||||
) : (
|
||||
<Grid
|
||||
container
|
||||
spacing={3}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
{displayVersions.map((version) => (
|
||||
<Grid key={version.id} size={{ xs: 12, sm: 6, md: 4 }}>
|
||||
<VersionCard
|
||||
id={version.id}
|
||||
name={version.name}
|
||||
imageUrl={
|
||||
version.imageUrl ||
|
||||
'https://via.placeholder.com/300x140?text=Minecraft'
|
||||
}
|
||||
version={version.version}
|
||||
onSelect={() => handleSelectVersion(version)}
|
||||
/>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
Reference in New Issue
Block a user