add: server status

This commit is contained in:
2025-07-07 02:53:21 +05:00
parent 2eda1d7806
commit 261b9ac253
6 changed files with 208 additions and 36 deletions

View File

@ -14,7 +14,11 @@ import { autoUpdater } from 'electron-updater';
import log from 'electron-log'; import log from 'electron-log';
import MenuBuilder from './menu'; import MenuBuilder from './menu';
import { resolveHtmlPath } from './util'; import { resolveHtmlPath } from './util';
import { initMinecraftHandlers, initAuthHandlers } from './minecraft-launcher'; import {
initMinecraftHandlers,
initAuthHandlers,
initServerStatusHandler,
} from './minecraft-launcher';
class AppUpdater { class AppUpdater {
constructor() { constructor() {
@ -44,9 +48,6 @@ if (isDebug) {
require('electron-debug').default(); require('electron-debug').default();
} }
// Инициализация обработчиков Minecraft
initMinecraftHandlers();
const installExtensions = async () => { const installExtensions = async () => {
const installer = require('electron-devtools-installer'); const installer = require('electron-devtools-installer');
const forceDownload = !!process.env.UPGRADE_EXTENSIONS; const forceDownload = !!process.env.UPGRADE_EXTENSIONS;
@ -77,6 +78,7 @@ const createWindow = async () => {
show: false, show: false,
width: 1024, width: 1024,
height: 728, height: 728,
autoHideMenuBar: true,
icon: getAssetPath('icon.png'), icon: getAssetPath('icon.png'),
webPreferences: { webPreferences: {
webSecurity: false, webSecurity: false,
@ -118,6 +120,7 @@ const createWindow = async () => {
initAuthHandlers(); initAuthHandlers();
initMinecraftHandlers(); initMinecraftHandlers();
initServerStatusHandler();
}; };
/** /**

View File

@ -18,12 +18,8 @@ import { spawn } from 'child_process';
import { AuthService } from './auth-service'; import { AuthService } from './auth-service';
// Константы // Константы
const COMFORT_DOWNLOAD_URL =
'https://github.com/DIKER0K/Comfort/releases/latest/download/Comfort.zip';
const GITHUB_API_RELEASE_URL =
'https://api.github.com/repos/DIKER0K/Comfort/releases/latest';
const COMFORT_VERSION_FILE = 'comfort_version.txt';
const AUTHLIB_INJECTOR_FILENAME = 'authlib-injector-1.2.5.jar'; const AUTHLIB_INJECTOR_FILENAME = 'authlib-injector-1.2.5.jar';
const MCSTATUS_API_URL = 'https://api.mcstatus.io/v2/status/java/';
// Создаем экземпляр сервиса аутентификации // Создаем экземпляр сервиса аутентификации
const authService = new AuthService(); const authService = new AuthService();
@ -769,3 +765,44 @@ export function initAuthHandlers() {
}, },
); );
} }
// Функция для получения статуса сервера
export function initServerStatusHandler() {
ipcMain.handle('get-server-status', async (event, { host, port }) => {
try {
// Формируем адрес с портом, если указан
const serverAddress = port ? `${host}:${port}` : host;
console.log(
`Запрос статуса сервера: ${MCSTATUS_API_URL}${serverAddress}`,
);
// Делаем запрос к API mcstatus.io
const response = await fetch(`${MCSTATUS_API_URL}${serverAddress}`);
// Проверяем статус ответа
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API вернул ошибку ${response.status}: ${errorText}`);
}
const data = await response.json();
console.log('Получен ответ от API:', data);
if (data.online) {
return {
success: true,
online: data.players?.online || 0,
max: data.players?.max || 0,
version: data.version?.name_clean || 'Unknown',
icon: data.icon || null, // Возвращаем иконку
motd: data.motd?.clean || '', // Название сервера
};
} else {
return { success: false, error: 'Сервер не доступен' };
}
} catch (error) {
console.error('Ошибка при получении статуса сервера:', error);
return { success: false, error: error.message };
}
});
}

View File

@ -4,7 +4,8 @@ export type Channels =
| 'ipc-example' | 'ipc-example'
| 'download-progress' | 'download-progress'
| 'launch-minecraft' | 'launch-minecraft'
| 'installation-status'; | 'installation-status'
| 'get-server-status';
const electronHandler = { const electronHandler = {
ipcRenderer: { ipcRenderer: {

View File

@ -9,6 +9,19 @@ import LaunchPage from './pages/LaunchPage';
import { ReactNode, useEffect, useState } from 'react'; import { ReactNode, useEffect, useState } from 'react';
import './App.css'; import './App.css';
// Переместите 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: 'fabric0.16.14',
};
const AuthCheck = ({ children }: { children: ReactNode }) => { const AuthCheck = ({ children }: { children: ReactNode }) => {
const [isAuthenticated, setIsAuthenticated] = useState<boolean | null>(null); const [isAuthenticated, setIsAuthenticated] = useState<boolean | null>(null);
@ -66,7 +79,7 @@ const App = () => {
path="/" path="/"
element={ element={
<AuthCheck> <AuthCheck>
<LaunchPage /> <LaunchPage launchOptions={launchOptions} />
</AuthCheck> </AuthCheck>
} }
/> />

View File

@ -0,0 +1,121 @@
import { Box, Typography, CircularProgress, Avatar } from '@mui/material';
import { useEffect, useState } from 'react';
interface ServerStatusProps {
serverIp: string;
serverPort?: number;
refreshInterval?: number; // Интервал обновления в миллисекундах
}
const ServerStatus = ({
serverIp,
serverPort,
refreshInterval = 60000, // По умолчанию обновление раз в минуту
}: ServerStatusProps) => {
const [serverStatus, setServerStatus] = useState<{
online: number;
max: number;
loading: boolean;
error: string | null;
icon: string | null;
motd: string;
}>({
online: 0,
max: 0,
loading: true,
error: null,
icon: null,
motd: '',
});
useEffect(() => {
// Функция для получения статуса сервера
const fetchServerStatus = async () => {
try {
setServerStatus((prev) => ({ ...prev, loading: true, error: null }));
console.log('Отправляем запрос на сервер с параметрами:', {
host: serverIp,
port: serverPort || 25565,
});
// Проверяем, что serverIp имеет значение
if (!serverIp) {
throw new Error('Адрес сервера не указан');
}
const result = await window.electron.ipcRenderer.invoke(
'get-server-status',
{
host: serverIp,
port: serverPort || 25565,
},
);
if (result.success) {
setServerStatus({
online: result.online,
max: result.max,
loading: false,
error: null,
icon: result.icon,
motd: result.motd || serverIp,
});
} else {
setServerStatus({
online: 0,
max: 0,
loading: false,
error: result.error || 'Неизвестная ошибка',
icon: null,
motd: '',
});
}
} catch (error) {
console.error('Ошибка при получении статуса сервера:', error);
setServerStatus((prev) => ({
...prev,
loading: false,
error: 'Ошибка при получении статуса сервера',
icon: null,
}));
}
};
// Загрузка при первом рендере
fetchServerStatus();
// Периодическое обновление
const interval = setInterval(fetchServerStatus, refreshInterval);
return () => clearInterval(interval);
}, [serverIp, serverPort, refreshInterval]);
return (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
{/* Отображаем иконку сервера или иконку по умолчанию */}
{serverStatus.icon ? (
<Avatar
src={serverStatus.icon}
alt={serverStatus.motd || 'Minecraft сервер'}
sx={{ width: '2em', height: '2em' }}
/>
) : (
<Avatar sx={{ width: '2em', height: '2em', bgcolor: 'primary.main' }}>
?
</Avatar>
)}
{serverStatus.loading ? (
<CircularProgress size={20} />
) : serverStatus.error ? (
<Typography color="error">Ошибка загрузки</Typography>
) : (
<Typography sx={{ fontWeight: 'bold' }}>
{serverStatus.online} / {serverStatus.max} игроков
</Typography>
)}
</Box>
);
};
export default ServerStatus;

View File

@ -8,6 +8,7 @@ import {
} from '@mui/material'; } from '@mui/material';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import ServerStatus from '../components/ServerStatus/ServerStatus';
declare global { declare global {
interface Window { interface Window {
@ -21,19 +22,21 @@ declare global {
} }
} }
const launchOptions = { // Определяем тип для props
downloadUrl: interface LaunchPageProps {
'https://github.com/DIKER0K/Comfort/releases/latest/download/Comfort.zip', launchOptions: {
apiReleaseUrl: 'https://api.github.com/repos/DIKER0K/Comfort/releases/latest', downloadUrl: string;
versionFileName: 'comfort_version.txt', apiReleaseUrl: string;
packName: 'Comfort', versionFileName: string;
memory: 4096, packName: string;
baseVersion: '1.21.4', memory: number;
serverIp: 'popa-popa.ru', baseVersion: string;
fabricVersion: 'fabric0.16.14', serverIp: string;
fabricVersion: string;
}; };
}
const LaunchPage = (launchOptions: any) => { const LaunchPage = ({ launchOptions }: LaunchPageProps) => {
const navigate = useNavigate(); const navigate = useNavigate();
const [isDownloading, setIsDownloading] = useState(false); const [isDownloading, setIsDownloading] = useState(false);
const [downloadProgress, setDownloadProgress] = useState(0); const [downloadProgress, setDownloadProgress] = useState(0);
@ -181,11 +184,15 @@ const LaunchPage = (launchOptions: any) => {
Добро пожаловать в лаунчер Добро пожаловать в лаунчер
</Typography> </Typography>
<Box sx={{ mb: 3 }}>
<ServerStatus
serverIp={launchOptions.serverIp}
refreshInterval={30000}
/>
</Box>
{isDownloading ? ( {isDownloading ? (
<Box sx={{ mb: 3, width: '100%' }}> <Box sx={{ mb: 3, width: '100%' }}>
{/* <Typography sx={{ mb: 1 }}>
Загрузка и установка: {downloadProgress}%
</Typography> */}
<Box sx={{ display: 'flex', alignItems: 'center' }}> <Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{ width: '100%', mr: 1 }}> <Box sx={{ width: '100%', mr: 1 }}>
<LinearProgress <LinearProgress
@ -201,16 +208,6 @@ const LaunchPage = (launchOptions: any) => {
>{`${Math.round(downloadProgress)}%`}</Typography> >{`${Math.round(downloadProgress)}%`}</Typography>
</Box> </Box>
</Box> </Box>
{/* {installMessage && (
<Typography variant="body1" sx={{ mt: 1, color: 'white' }}>
{installMessage}
</Typography>
)}
{installStep && (
<Typography variant="body2" sx={{ color: 'white' }}>
Шаг: {installStep}
</Typography>
)} */}
</Box> </Box>
) : ( ) : (
<Button <Button