add: server status
This commit is contained in:
@ -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();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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 };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -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: {
|
||||||
|
@ -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>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
121
src/renderer/components/ServerStatus/ServerStatus.tsx
Normal file
121
src/renderer/components/ServerStatus/ServerStatus.tsx
Normal 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;
|
@ -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
|
||||||
|
Reference in New Issue
Block a user