65 Commits

Author SHA1 Message Date
SashegDev 82391e10ea feat(ui): добавляем прогресс-коллбэк и автообновление лаунчера
- добавили ProgressCallback в PackDownloader для отслеживания прогресса
- SSE эндпоинт /instances/{name}/install/stream для стриминга прогресса
- добавили checkLauncherUpdate() в WebServer для обновления самого лаунчера
- теперь при запуске UI проверяются обновления лаунчера автоматически
2026-05-05 08:53:01 +00:00
SashegDev 1934199ba8 fix: исправляем баг с версией сборки
раньше latestVersion вычислялся как currentVersion + 1
теперь честно получаем версию с сервера через PackDownloader.checkForUpdates()

так же обновили места в LaunchMenu и UpdateMenu где используется этот метод
2026-05-05 08:42:51 +00:00
SashegDev 526a24a16a feat(ui): добавляем верификацию файлов и кнопку ОБНОВИТЬ
- спиздили апи файлы из alpha (ApiResponse, AuthService, InstanceService, LaunchService)
- добавили в InstanceInfo поля isServerPack и serverPackName
- CSS: добавили оранжевую .btn-update кнопку
- JS: при загрузке инстанса проверяем целостность (verify) и обновления (updates)
- Кнопка ИГРАТЬ теперь меняется на ОБНОВИТЬ если есть косяки
- ОБНОВИТЬ докачивает/обновляет файлы через повторный install

всё как ты хотел, красава
2026-05-05 08:21:10 +00:00
sasheg dev 96baeeea68 Merge pull request #5 from SashegDev/ui
feat(ui): add Web UI with JavaFX, install service, and new tests
2026-05-05 09:54:09 +03:00
SashegDev 28995adce8 feat(ui): add Web UI with JavaFX, install service, and new tests
- Add JavaFX WebView for native window UI (fallback to TUI on headless)
- Create WebServer with Javalin HTTP server
- Add webapp with dark theme and grid animation
- Create InstallService for ZernMC pack installation
- Integrate CLI installation logic via PackDownloader
- Add verifyHashes() using /pack/{name}/diff endpoint
- Add API endpoints: /instances/zernmc/install, /instances/{name}/updates, /instances/{name}/verify, /instances/{name}/playtime
- Add 14 new tests (WebServerTest, HeadlessDetectionTest, InstanceServiceTest)
- Total 44 tests now passing
2026-05-05 06:52:13 +00:00
SashegDev c9ed825686 feat(ui): add Web UI with JavaFX, install service, and new tests
- Add JavaFX WebView for native window UI (fallback to TUI on headless)
- Create WebServer with Javalin HTTP server
- Add webapp with dark theme and grid animation
- Create InstallService for ZernMC pack installation
- Integrate CLI installation logic via PackDownloader
- Add verifyHashes() using /pack/{name}/diff endpoint
- Add API endpoints: /instances/zernmc/install, /instances/{name}/updates, /instances/{name}/verify, /instances/{name}/playtime
- Add 14 new tests (WebServerTest, HeadlessDetectionTest, InstanceServiceTest)
- Total 44 tests now passing
2026-05-05 06:48:27 +00:00
SashegDev d0b4e187c8 feat(api): add internal API foundation for UI
- Create api package with AuthService, InstanceService, LaunchService
- Add ApiResponse<T> model for consistent responses
- Create LauncherAPI central facade for all services
- Update Main.java to use new API for session checking
- All services compile successfully
2026-05-05 04:12:39 +00:00
sasheg dev 3f2cb6662a Немного подправил инфу 2026-05-05 02:16:06 +03:00
sasheg dev 11513fbf13 Merge pull request #4 from SashegDev/alpha
Ептить медж из альфы! СПУСТЯ СТОЛЬКО ВРЕМЕНИ ЕБААААТ
2026-05-05 02:12:31 +03:00
SashegDev f2d3de82f7 refactor(launch): dynamic version JSON parsing for Forge/NeoForge compatibility
- Replace hardcoded Forge/NeoForge args with version.json parsing
- Add VersionManifest.java — parses mainClass, arguments, libraries from JSON
- Implement rule matching for OS-specific library/argument filtering
- Build classpath dynamically from manifest libraries with fallback resolution
- Resolve game args with variable substitution (${version_name}, ${game_directory}, etc.)
- Auto-discover version.json path with multiple candidate formats
- Support all Forge versions (1.12.2 through 1.21+) and NeoForge out of the box
2026-05-04 22:58:49 +00:00
SashegDev b4431702dc feat: add NeoForge support, fix Forge installPack bug, update server proxy
- Fix MinecraftLib.installPack() returning false for Forge (was dead code)
- Add NeoForgeInstaller.java with installer download and execution
- Update LaunchCommandBuilder with NeoForge JVM args, classpath, launch args
- Update LaunchMenu with NeoForge option, version selector, support check
- Update Instance.java loader type comment (vanilla, fabric, forge, neoforge)
- Update PackDownloader to handle neoforge loader type
- Update ZHttpClient with NEOFORGE_MAVEN service type and detection
- Add NeoForge proxy endpoints (/proxy/neoforge/versions, /proxy/neoforge/maven)
- Add maven.neoforged.net to proxy allowed_domains
- Add asset_index to PackMeta model and pack_manager scanning
- Include asset_index in /packs list endpoint response
2026-05-04 22:53:22 +00:00
SashegDev cd2cf44d9c test(client): add JUnit 5 tests (30 tests) — unit + integration
- Add JUnit 5 dependency to pom.xml with surefire plugin
- Add setBaseUrl() to ZHttpClient for test server override
- AuthManagerParsingTest (7 tests): error extraction from JSON responses
  (simple detail, validation array, multiple errors, plain text, truncation)
- PackDownloaderParsingTest (13 tests): JSON contract for packs, manifests,
  diffs, file info, ServerPack toString
- ServerIntegrationTest (10 tests): real Java client ↔ real FastAPI server
  (register, login, duplicate, wrong password, /admin/me, validate token,
  refresh, packs auth, pack manifest public, launcher version)
- Integration tests auto-start test server via venv python3 subprocess
  on random port with isolated temp DB, graceful skip if unavailable

All 30 tests pass, 0 failures
2026-05-04 22:40:10 +00:00
SashegDev 8939e24e69 test(server): add client-facing endpoint tests (20 tests), fix pack contract assertions
- Add test_client.py with comprehensive client-server contract tests:
  - TestAuthFlowClient: full register → login → refresh → validate → /admin/me → logout lifecycle
  - TestPacksClientContract: /packs response fields matching ServerPack.java
  - TestPackManifestClientContract: /pack/{name} fields matching PackManifest.java
  - TestPackDiffClientContract: /pack/{name}/diff matching DiffResponse/FileInfo.java
    (all-new, no-changes, outdated-file, extra-local-file scenarios)
  - TestPackFileDownload: file serving, 404, path traversal security
  - TestPackPermissions: auth/pass requirements for /packs and /diff
  - TestLauncherVersion: /launcher/version endpoint
  - TestProxyEndpoints: /proxy/status, /proxy/fabric/versions/loader
- Add logged_in_user_with_pass fixture (role=1) for pack-related tests
- Add pack_fixture: creates temp pack with mod file, scans it, cleans up
- Fix manifest test: files don't have 'url' field (only in diff response)
- Fix /pack/{name} test: endpoint is public, no auth required

Total: 67 tests passing (47 existing + 20 new)
2026-05-04 22:28:12 +00:00
SashegDev c0310ed573 test(server): add comprehensive test suite (47 tests), fix DB lock and schema bugs
- Add pytest test suite: test_auth.py, test_admin.py, test_pass.py,
  test_proxy.py, test_rate_limit.py, test_client_contract.py
- Fix SQLite 'database is locked' errors: moved log_audit() calls outside
  with get_db() blocks in register, login, logout, refresh, activate_pass
- Enable WAL mode and busy_timeout in get_db() for concurrent access
- Fix /admin/me: removed non-existent 'email' column from query
- Fix /admin/users list: disambiguated activated_at column in JOIN query
- Fix /auth/refresh: now returns refresh_token + expires_in + username/uuid/role
  to match AuthManager.AuthSession expectations; revokes old refresh token
- Fix conftest.py: unique usernames per test to avoid conflicts
- All 47 tests passing
2026-05-04 22:14:06 +00:00
SashegDev c96b502ad4 fix(server,security): add ban check to validate_token, replace rate_limit DB with TTLCache 2026-05-04 21:12:35 +00:00
SashegDev bfcffdd88d chore(server): remove unused models, delete http_logger.py, rename viev_logs.py → view_logs.py 2026-05-04 21:10:11 +00:00
SashegDev 331fc9a863 refactor(server): clean main.py — remove duplicate imports, dead code, unify logging, fix proxy lifecycle 2026-05-04 21:09:10 +00:00
SashegDev e347c042d5 feat(server): add /auth/pass/activate endpoint for pass code activation 2026-05-04 21:06:56 +00:00
SashegDev bb564e6e9b feat(server): connect admin_router to FastAPI app 2026-05-04 21:06:02 +00:00
SashegDev 6f53002266 fix(server): add role aliases in roles.py to fix broken admin_router imports 2026-05-04 21:04:44 +00:00
SashegDev 9688509df5 fix(pom.xml): correct launch4j JAR path for exe build 2026-05-04 20:52:28 +00:00
SashegDev efc4b086d1 fix(TUI): proper arrow key handling — parse ESC sequences instead of treating as Esc 2026-05-04 20:39:29 +00:00
SashegDev 2cdc438411 just workin on the todo 2026-05-04 20:26:27 +00:00
Sashegdev b60e414d37 last commit to uuuuh idl 2026-05-04 15:19:46 +00:00
Sashegdev 10ec8625b9 The fuck was hapanned тут 2026-04-22 12:54:57 +00:00
Sashegdev f24cc078c5 Merge branch 'main' into alpha 2026-04-22 15:26:39 +03:00
Sashegdev adde40d921 Коммит, для того что бы если что роллбекать 2026-04-22 12:23:51 +00:00
Sashegdev 6bf6c1634a Фиксы проходок (нормально, в отличии от main ветки)
ОНО РАБОТАЕТ СУКАААА
2026-04-20 19:30:17 +00:00
Sashegdev 98462ba4a3 Update issue templates 2026-04-20 19:59:07 +03:00
Sashegdev 11ec84fe24 Create LICENSE 2026-04-20 19:57:52 +03:00
Sashegdev 8b56652a73 test penis 2026-04-09 18:13:21 +00:00
Sashegdev d7a6eb760e fixes 2026-04-09 18:03:00 +00:00
Sashegdev c6dd215e9b рефакторинг + новая система модерации 2026-04-09 17:28:48 +00:00
Sashegdev a3f9871d6e СУКА ЛАСТ ФИКСЫ ДЛЯ ПРОХОДОК (логин работает) 2026-04-08 20:22:47 +00:00
Sashegdev 2b6cb6b3ad ДА БЛЯ Я ЗАБЕАЛСЯ ФИКСИТЬ ПОМОГИТЕ Я КОНЧЕННЫЫЫЙ 2026-04-08 20:16:51 +00:00
Sashegdev cca6ef3eca SuperMinor Fixes (надеюсь последние для аккаунтов) 2026-04-08 20:04:52 +00:00
Sashegdev 8733e359e6 Minor fixes(важные блять) 2026-04-08 20:02:09 +00:00
Sashegdev 89c0057759 Server Fixes 2026-04-08 19:56:38 +00:00
Sashegdev bf26baaf93 1.0.7 типоооо и фиксы 2026-04-08 19:45:15 +00:00
Sashegdev 13a43a01ef utf-8 рефактор чутка 2026-04-07 19:00:42 +00:00
Sashegdev 296f564b39 небольшой рефактор 2026-04-07 18:54:16 +00:00
Sashegdev de703a4ddd ВАЖНИ ФИКСЕС 2026-04-07 18:40:51 +00:00
Sashegdev a501329956 Изменил версию и немного фиксов 2026-04-07 18:28:26 +00:00
Sashegdev 5516aeb12f Readme модификейшин 2026-04-07 18:08:11 +00:00
Sashegdev 7b48ae2ab6 Merge remote-tracking branch 'refs/remotes/origin/main' 2026-04-07 18:01:49 +00:00
Sashegdev 9bee361ea4 Попытка добавления проходок, аккаунтов, а так же доработка прокси 2026-04-07 17:50:29 +00:00
Sashegdev 8cbad9af96 Update README.md 2026-04-07 18:11:54 +03:00
Sashegdev 33cb7830e3 Орфографическая правка 2026-04-07 12:24:06 +03:00
Sashegdev 920330bbab REAMDE.md update, мяу 2026-04-07 11:17:36 +03:00
Sashegdev c03d7a788f ДОБАВЛЕНИЕ ПРОКСИ РЕЖИМА ЙОООУ 1.0.5 2026-04-06 19:57:32 +00:00
Sashegdev b47793b618 Багфиксы ClassPath 2026-04-06 18:17:07 +00:00
Sashegdev 94968e8e77 Smol Fixes Yoooo 2026-04-06 17:00:39 +00:00
Sashegdev 0b4af1353d Both | БЛЯЯЯ ЗАГРУЗКА ПАКОВ С СЕРВЕРА СЮДААА 2026-04-06 00:32:36 +00:00
Sashegdev 4edbe7e910 Server BugFixes + убрал генерацию sevrer команды т.к это уже в клиенте лол 2026-04-05 22:25:43 +00:00
Sashegdev 3d8313f7d2 Немного рефактора 2026-04-05 18:45:31 +00:00
Sashegdev e21fd922ab Попытка заставить работать Forge 2026-04-05 16:18:39 +00:00
Sashegdev b29222af68 ФАБРИК ПОДДЕРЖКАААААААА 2026-04-05 15:43:16 +00:00
Sashegdev ac3ce1800f Починил загрузку ассетов, добавлена оптимизация
запуск Vanilla версий работает
2026-04-05 14:56:01 +00:00
Sashegdev 2babe53e99 uuuh почему бы и нет 2026-04-05 10:47:28 +00:00
Sashegdev 369f8c2f9d Изменение получения self-version 2026-04-05 10:46:32 +00:00
Sashegdev c0a85658f4 КЛИЕНТ ЛАУНЧЕРА ЙОООО 2026-04-05 00:18:57 +00:00
Sashegdev 7568e34d91 перенос readme т.к я криворукий 2026-04-04 14:59:00 +00:00
Sashegdev 7670edbff7 server update 2026-04-04 14:57:15 +00:00
Sashegdev cf4a5c74e5 test 2026-04-04 14:55:18 +00:00
Sashegdev 1a5336a996 first commit 2026-04-04 14:49:24 +00:00
7 changed files with 22 additions and 248 deletions
+1 -40
View File
@@ -42,21 +42,12 @@
<exclude>META-INF/*.RSA</exclude> <exclude>META-INF/*.RSA</exclude>
</excludes> </excludes>
</filter> </filter>
<filter>
<artifact>org.openjfx:*</artifact>
<excludes>
<exclude>**/*</exclude>
</excludes>
</filter>
</filters> </filters>
<dependencySet> <dependencySet>
<outputDirectory>/</outputDirectory> <outputDirectory>/</outputDirectory>
<useProjectArtifact>false</useProjectArtifact> <useProjectArtifact>false</useProjectArtifact>
<unpack>true</unpack> <unpack>true</unpack>
<scope>runtime</scope> <scope>runtime</scope>
<excludes>
<exclude>org.openjfx:*</exclude>
</excludes>
</dependencySet> </dependencySet>
</configuration> </configuration>
</execution> </execution>
@@ -94,16 +85,11 @@
<configuration> <configuration>
<outfile>../server/builds/ZernMCLauncher-${project.version}.exe</outfile> <outfile>../server/builds/ZernMCLauncher-${project.version}.exe</outfile>
<jar>../server/builds/ZernMCLauncher.jar</jar> <jar>../server/builds/ZernMCLauncher.jar</jar>
<headerType>gui</headerType> <headerType>console</headerType>
<dontWrapJar>false</dontWrapJar> <dontWrapJar>false</dontWrapJar>
<jre> <jre>
<path>jre21</path> <path>jre21</path>
<minVersion>21</minVersion> <minVersion>21</minVersion>
<opts>
<opt>--module-path=lib-javafx</opt>
<opt>--add-modules=javafx.controls,javafx.web</opt>
<opt>--add-reads=javafx.graphics=ALL-UNNAMED</opt>
</opts>
</jre> </jre>
<versionInfo> <versionInfo>
<fileVersion>${project.version}.0</fileVersion> <fileVersion>${project.version}.0</fileVersion>
@@ -119,9 +105,6 @@
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
<configuration>
<skip>${skip.launch4j}</skip>
</configuration>
</plugin> </plugin>
<plugin> <plugin>
<artifactId>maven-antrun-plugin</artifactId> <artifactId>maven-antrun-plugin</artifactId>
@@ -138,9 +121,6 @@
<copy> <copy>
<fileset /> <fileset />
</copy> </copy>
<copy>
<fileset />
</copy>
<zip /> <zip />
</target> </target>
</configuration> </configuration>
@@ -166,22 +146,6 @@
<server.url>http://87.120.187.36:1582</server.url> <server.url>http://87.120.187.36:1582</server.url>
</properties> </properties>
</profile> </profile>
<profile>
<id>win</id>
<properties>
<os.suffix>win</os.suffix>
<javafx.classifier>win</javafx.classifier>
<skip.launch4j>false</skip.launch4j>
</properties>
</profile>
<profile>
<id>linux</id>
<properties>
<os.suffix>linux</os.suffix>
<javafx.classifier>linux</javafx.classifier>
<skip.launch4j>true</skip.launch4j>
</properties>
</profile>
</profiles> </profiles>
<dependencies> <dependencies>
<dependency> <dependency>
@@ -210,10 +174,7 @@
<mainClass>me.sashegdev.zernmc.launcher.Main</mainClass> <mainClass>me.sashegdev.zernmc.launcher.Main</mainClass>
<maven.compiler.source>21</maven.compiler.source> <maven.compiler.source>21</maven.compiler.source>
<project.organization.name>ZernMC</project.organization.name> <project.organization.name>ZernMC</project.organization.name>
<javafx.classifier>win</javafx.classifier>
<skip.launch4j>false</skip.launch4j>
<maven.compiler.target>21</maven.compiler.target> <maven.compiler.target>21</maven.compiler.target>
<os.suffix>win</os.suffix>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.inceptionYear>2026</project.inceptionYear> <project.inceptionYear>2026</project.inceptionYear>
</properties> </properties>
+6 -84
View File
@@ -17,9 +17,6 @@
<project.inceptionYear>2026</project.inceptionYear> <project.inceptionYear>2026</project.inceptionYear>
<project.description>ZernMC Launcher - just a minimalistic launcher by SashegDev</project.description> <project.description>ZernMC Launcher - just a minimalistic launcher by SashegDev</project.description>
<mainClass>me.sashegdev.zernmc.launcher.Main</mainClass> <mainClass>me.sashegdev.zernmc.launcher.Main</mainClass>
<javafx.classifier>win</javafx.classifier>
<os.suffix>win</os.suffix>
<skip.launch4j>false</skip.launch4j>
</properties> </properties>
<dependencies> <dependencies>
@@ -77,27 +74,11 @@
<groupId>org.openjfx</groupId> <groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId> <artifactId>javafx-controls</artifactId>
<version>21.0.2</version> <version>21.0.2</version>
<classifier>win</classifier>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.openjfx</groupId> <groupId>org.openjfx</groupId>
<artifactId>javafx-web</artifactId> <artifactId>javafx-web</artifactId>
<version>21.0.2</version> <version>21.0.2</version>
<classifier>win</classifier>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>21.0.2</version>
<classifier>linux</classifier>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-web</artifactId>
<version>21.0.2</version>
<classifier>linux</classifier>
<scope>runtime</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
@@ -149,22 +130,12 @@
<exclude>META-INF/*.RSA</exclude> <exclude>META-INF/*.RSA</exclude>
</excludes> </excludes>
</filter> </filter>
<!-- Исключаем JavaFX из shade полностью (он будет в lib-javafx) -->
<filter>
<artifact>org.openjfx:*</artifact>
<excludes>
<exclude>**/*</exclude>
</excludes>
</filter>
</filters> </filters>
<dependencySet> <dependencySet>
<outputDirectory>/</outputDirectory> <outputDirectory>/</outputDirectory>
<useProjectArtifact>false</useProjectArtifact> <useProjectArtifact>false</useProjectArtifact>
<unpack>true</unpack> <unpack>true</unpack>
<scope>runtime</scope> <scope>runtime</scope>
<excludes>
<exclude>org.openjfx:*</exclude>
</excludes>
</dependencySet> </dependencySet>
</configuration> </configuration>
</execution> </execution>
@@ -192,14 +163,11 @@
</executions> </executions>
</plugin> </plugin>
<!-- Launch4j для создания .exe (только для Windows) --> <!-- Launch4j для создания .exe -->
<plugin> <plugin>
<groupId>com.akathist.maven.plugins.launch4j</groupId> <groupId>com.akathist.maven.plugins.launch4j</groupId>
<artifactId>launch4j-maven-plugin</artifactId> <artifactId>launch4j-maven-plugin</artifactId>
<version>2.5.0</version> <version>2.5.0</version>
<configuration>
<skip>${skip.launch4j}</skip>
</configuration>
<executions> <executions>
<execution> <execution>
<id>l4j</id> <id>l4j</id>
@@ -210,16 +178,11 @@
<configuration> <configuration>
<outfile>../server/builds/ZernMCLauncher-${project.version}.exe</outfile> <outfile>../server/builds/ZernMCLauncher-${project.version}.exe</outfile>
<jar>../server/builds/ZernMCLauncher.jar</jar> <jar>../server/builds/ZernMCLauncher.jar</jar>
<headerType>gui</headerType> <headerType>console</headerType>
<dontWrapJar>false</dontWrapJar> <dontWrapJar>false</dontWrapJar>
<jre> <jre>
<path>jre21</path> <path>jre21</path>
<minVersion>21</minVersion> <minVersion>21</minVersion>
<opts>
<opt>--module-path=lib-javafx</opt>
<opt>--add-modules=javafx.controls,javafx.web</opt>
<opt>--add-reads=javafx.graphics=ALL-UNNAMED</opt>
</opts>
</jre> </jre>
<versionInfo> <versionInfo>
<fileVersion>${project.version}.0</fileVersion> <fileVersion>${project.version}.0</fileVersion>
@@ -255,22 +218,11 @@
<fileset dir="${user.home}/launcher/jre/jre21"/> <fileset dir="${user.home}/launcher/jre/jre21"/>
</copy> </copy>
<!-- Копируем JavaFX JAR в builds --> <!-- Создаём zip только с .exe и jre21 (без .jar и build.version) -->
<copy todir="../server/builds/lib-javafx" overwrite="true"> <zip destfile="../server/builds/ZernMCLauncher-${project.version}.zip"
<fileset dir="${project.build.directory}/lib-javafx"/>
</copy>
<!-- Копируем shell script для Linux -->
<copy file="${project.basedir}/src/main/resources/launcher.sh"
todir="../server/builds"
overwrite="true"/>
<chmod file="../server/builds/launcher.sh" perm="+x"/>
<!-- Создаём zip с .exe, jre21, lib-javafx и launcher.sh (без .jar и build.version) -->
<zip destfile="../server/builds/ZernMCLauncher-${project.version}-${os.suffix}.zip"
basedir="../server/builds" basedir="../server/builds"
includes="ZernMCLauncher.exe,ZernMCLauncher.jar,jre21/**,lib-javafx/**,launcher.sh" includes="ZernMCLauncher.exe,jre21/**"
excludes="build.version"/> excludes="*.jar,build.version"/>
</target> </target>
</configuration> </configuration>
</execution> </execution>
@@ -302,35 +254,5 @@
<server.url>http://87.120.187.36:1582</server.url> <server.url>http://87.120.187.36:1582</server.url>
</properties> </properties>
</profile> </profile>
<!-- ==================== WINDOWS BUILD ==================== -->
<profile>
<id>win</id>
<activation>
<os>
<family>windows</family>
</os>
</activation>
<properties>
<javafx.classifier>win</javafx.classifier>
<os.suffix>win</os.suffix>
<skip.launch4j>false</skip.launch4j>
</properties>
</profile>
<!-- ==================== LINUX BUILD ==================== -->
<profile>
<id>linux</id>
<activation>
<os>
<family>unix</family>
</os>
</activation>
<properties>
<javafx.classifier>linux</javafx.classifier>
<os.suffix>linux</os.suffix>
<skip.launch4j>true</skip.launch4j>
</properties>
</profile>
</profiles> </profiles>
</project> </project>
@@ -34,7 +34,6 @@ public class Main {
startWebUI(args); startWebUI(args);
} catch (Exception e) { } catch (Exception e) {
System.err.println(ZAnsi.red("UI не запустился: " + e.getMessage())); System.err.println(ZAnsi.red("UI не запустился: " + e.getMessage()));
e.printStackTrace();
System.out.println(ZAnsi.yellow("Переключаюсь на режим TUI...")); System.out.println(ZAnsi.yellow("Переключаюсь на режим TUI..."));
runTUI(args); runTUI(args);
} }
@@ -71,16 +70,12 @@ public class Main {
// Даем серверу время запуститься // Даем серверу время запуститься
Thread.sleep(1000); Thread.sleep(1000);
// Проверяем headless перед запуском JavaFX (только для не-Windows систем) // Проверяем headless перед запуском JavaFX
if (!System.getProperty("os.name").toLowerCase().contains("win")) { if (java.awt.GraphicsEnvironment.isHeadless()) {
boolean isHeadless = java.awt.GraphicsEnvironment.isHeadless(); System.out.println(ZAnsi.yellow("Дисплей недоступен, переключаюсь на TUI..."));
String display = System.getenv("DISPLAY"); WebServer.stop();
if (isHeadless && (display == null || display.isEmpty())) { runTUI(args);
System.out.println(ZAnsi.yellow("Дисплей недоступен, переключаюсь на TUI...")); return;
WebServer.stop();
runTUI(args);
return;
}
} }
// Проверка обновлений лаунчера // Проверка обновлений лаунчера
@@ -186,20 +181,12 @@ public class Main {
try { try {
String javaPath = System.getProperty("java.home") + "/bin/java"; String javaPath = System.getProperty("java.home") + "/bin/java";
String jarPath = getCurrentJarPath().toAbsolutePath().toString(); String jarPath = getCurrentJarPath().toAbsolutePath().toString();
String launcherDir = jarPath.substring(0, jarPath.lastIndexOf(java.io.File.separator));
String javafxPath = launcherDir + java.io.File.separator + "lib-javafx";
System.out.println(ZAnsi.brightGreen("Перезапуск лаунчера с новой версией...")); System.out.println(ZAnsi.brightGreen("Перезапуск лаунчера с новой версией..."));
ProcessBuilder pb = new ProcessBuilder( new ProcessBuilder(javaPath, "-jar", jarPath)
javaPath, .inheritIO()
"--module-path=" + javafxPath, .start();
"--add-modules=javafx.controls,javafx.web",
"--add-reads=javafx.graphics=ALL-UNNAMED",
"-jar", jarPath
);
pb.inheritIO();
pb.start();
System.exit(0); System.exit(0);
} catch (Exception e) { } catch (Exception e) {
@@ -17,6 +17,11 @@ public class UIWindow extends Application {
private static int port; private static int port;
public static void start(int port) { public static void start(int port) {
// Backup проверка headless
if (java.awt.GraphicsEnvironment.isHeadless()) {
throw new RuntimeException("Headless environment - no display available");
}
UIWindow.port = port; UIWindow.port = port;
UIWindow.url = "http://localhost:" + port; UIWindow.url = "http://localhost:" + port;
Application.launch(UIWindow.class); Application.launch(UIWindow.class);
-12
View File
@@ -1,12 +0,0 @@
#!/bin/bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
JAVA_HOME="$SCRIPT_DIR/jre21"
JAVA="$JAVA_HOME/bin/java"
JAVAFX_PATH="$SCRIPT_DIR/lib-javafx"
exec "$JAVA" \
--module-path="$JAVAFX_PATH" \
--add-modules=javafx.controls,javafx.web \
--add-reads=javafx.graphics=ALL-UNNAMED \
-jar "$SCRIPT_DIR/ZernMCLauncher.jar" "$@"
@@ -686,30 +686,6 @@ body {
font-size: 13px; font-size: 13px;
} }
.progress-label {
margin-bottom: 8px;
font-weight: 500;
}
.progress-file {
font-size: 12px;
color: var(--text-muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.progress-fill.animated {
background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary), var(--accent-primary));
background-size: 200% 100%;
animation: progressShimmer 1.5s ease-in-out infinite;
}
@keyframes progressShimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
/* ==================== LOADING ==================== */ /* ==================== LOADING ==================== */
.loading-overlay { .loading-overlay {
position: fixed; position: fixed;
+1 -66
View File
@@ -325,43 +325,7 @@ class App {
} }
this.addLog('Обновление сборки...', 'info'); this.addLog('Обновление сборки...', 'info');
this.enablePlayButton(false); this.showProgress('Обновление сборки...');
const progressContainer = this.showAnimatedProgress('Обновление сборки...');
let eventSource = null;
let progressData = { totalFiles: 0, downloadedFiles: 0 };
try {
eventSource = new EventSource(`/api/instances/${this.currentInstance.name}/install/stream`);
eventSource.onmessage = (e) => {
try {
const data = JSON.parse(e.data);
if (data.phase === 'starting') {
progressData.totalFiles = data.totalFiles || 0;
this.updateAnimatedProgress(progressContainer, `Загрузка: 0/${progressData.totalFiles} файлов`, 5);
} else if (data.phase === 'downloading') {
progressData.downloadedFiles = data.downloadedFiles || 0;
const total = data.totalFiles || progressData.totalFiles || 1;
const percent = Math.round((progressData.downloadedFiles / total) * 100);
const fileName = data.currentFile ? data.currentFile.split('/').pop() : '';
const filePercent = data.filePercent || 0;
this.updateAnimatedProgress(progressContainer,
`Файл ${progressData.downloadedFiles}/${total} (${percent}%)`,
percent,
fileName,
filePercent
);
} else if (data.phase === 'complete') {
this.updateAnimatedProgress(progressContainer, 'Готово!', 100);
} else if (data.phase === 'error') {
this.addLog('Ошибка: ' + (data.message || 'неизвестная ошибка'), 'error');
}
} catch (err) {}
};
} catch (e) {
console.log('SSE not available, using fallback progress');
}
const result = await this.request('/instances/zernmc/install', { const result = await this.request('/instances/zernmc/install', {
method: 'POST', method: 'POST',
@@ -371,10 +335,6 @@ class App {
}) })
}); });
if (eventSource) {
eventSource.close();
}
this.hideProgress(); this.hideProgress();
if (result.success) { if (result.success) {
@@ -402,31 +362,6 @@ class App {
} }
} }
showAnimatedProgress(text) {
const progress = document.getElementById('download-progress');
const progressText = document.getElementById('progress-text');
const progressFill = document.getElementById('progress-fill');
progress.classList.remove('hidden');
progressText.innerHTML = `<div class="progress-label">${text}</div>
<div class="progress-file"></div>`;
progressFill.style.width = '5%';
progressFill.classList.add('animated');
return { container: progress, text: progressText, fill: progressFill };
}
updateAnimatedProgress(progressContainer, text, percent, fileName = '', filePercent = 0) {
const { text: progressText, fill: progressFill } = progressContainer;
if (fileName) {
progressText.innerHTML = `<div class="progress-label">${text}</div>
<div class="progress-file">${fileName} (${filePercent}%)</div>`;
} else {
progressText.innerHTML = `<div class="progress-label">${text}</div>`;
}
progressFill.style.width = percent + '%';
}
// ==================== DOWNLOAD MODAL ==================== // ==================== DOWNLOAD MODAL ====================
async showDownloadModal() { async showDownloadModal() {
document.getElementById('download-modal').classList.remove('hidden'); document.getElementById('download-modal').classList.remove('hidden');