Compare commits

..
Author SHA1 Message Date
Burning_TNT 738000ff39 移除无用类与方法 (#3132)
* Remove useless classes.

* Remove useless methods
2024-08-25 03:03:48 +08:00
ZkiteflyandGlavo 9b5445b686 修复模组详情页图标问题 (#3227)
* Update ModListPageSkin.java

* Update ModListPageSkin.java

* Update ModListPageSkin.java

* Update ModListPageSkin.java

* Update ModListPageSkin.java

* Update ModListPageSkin.java

* Update ModListPageSkin.java

* Update ModListPageSkin.java

* Update ModListPageSkin.java

* Update ModListPageSkin.java

* Update ModListPageSkin.java

* update

* 调整判断逻辑

* update

* update

---------

Co-authored-by: Glavo <zjx001202@gmail.com>
2024-08-25 02:55:55 +08:00
Haowei Wen abf58b2c20 Fix missing i18n key curse.category.7418 (#3260) 2024-08-24 23:44:13 +08:00
Zkitefly fd97f6c321 支持 Forge 官方源 (#3259)
* Revert "Revert "支持 Forge 官方源 (#3251)" (#3258)"

This reverts commit abd37a093a.

* Update ForgeVersionList.java
2024-08-24 23:43:50 +08:00
Glavo abd37a093a Revert "支持 Forge 官方源 (#3251)" (#3258)
This reverts commit 0f0cf55509.
2024-08-24 14:50:41 +08:00
Zkitefly 0f0cf55509 支持 Forge 官方源 (#3251)
* 支持 Forge 官方源

* 更新 ForgeVersionList.java

* 更新 ForgeVersionList.java
2024-08-24 03:32:45 +08:00
ZkiteflyandGlavo 95a1496389 Fix #3223: 正确解析包含 hash 的版本号 (#3224)
* Fix #3223

* 使用正则表达式删除内容

* 添加日志

添加日志

* Update GameVersion.java

* Update GameVersion.java

* update

* update

* update

---------

Co-authored-by: Glavo <zjx001202@gmail.com>
2024-08-20 01:08:49 +08:00
13 changed files with 83 additions and 211 deletions

No files matched your search

@@ -1,86 +0,0 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.countly;
import org.jackhuang.hmcl.util.io.HttpRequest;
import java.io.IOException;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Locale;
import static org.jackhuang.hmcl.util.Pair.pair;
public class Countly {
private String deviceId;
private String endpoint;
private String serverURL;
public void sendMetric(String metrics) throws IOException {
HttpRequest.GET(serverURL + endpoint,
pair("begin_session", "1"),
pair("session_id", "1"),
pair("metrics", metrics),
pair("device_id", deviceId),
pair("timestamp", Long.toString(System.currentTimeMillis())),
pair("tz", Integer.toString(getTimezoneOffset())),
pair("hour", Integer.toString(currentHour())),
pair("dow", Integer.toString(currentDayOfWeek())),
pair("app_key", APP_KEY),
pair("sdk_name", "java-native"),
pair("sdk_version", "20.11.1"))
.getString();
}
private static int getTimezoneOffset() {
return ZonedDateTime.now().getOffset().getTotalSeconds() / 60;
}
private static String getLocale() {
final Locale locale = Locale.getDefault();
return locale.getLanguage() + "_" + locale.getCountry();
}
private static int currentHour() {
return Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
}
private int currentDayOfWeek() {
int day = Calendar.getInstance().get(Calendar.DAY_OF_WEEK);
switch (day) {
case Calendar.SUNDAY:
return 0;
case Calendar.MONDAY:
return 1;
case Calendar.TUESDAY:
return 2;
case Calendar.WEDNESDAY:
return 3;
case Calendar.THURSDAY:
return 4;
case Calendar.FRIDAY:
return 5;
case Calendar.SATURDAY:
return 6;
}
return 0;
}
private static final String APP_KEY = "";
}
@@ -5,14 +5,8 @@ import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.platform.Architecture;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
import java.io.File;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import static org.jackhuang.hmcl.util.Lang.mapOf;
import static org.jackhuang.hmcl.util.Pair.pair;
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
public class CrashReport {
@@ -20,24 +14,16 @@ public class CrashReport {
private final Throwable throwable;
private final String stackTrace;
private boolean nonFatal;
public CrashReport(Thread thread, Throwable throwable) {
this.thread = thread;
this.throwable = throwable;
stackTrace = StringUtils.getStackTrace(throwable);
nonFatal = false;
}
public Throwable getThrowable() {
return this.throwable;
}
public CrashReport setNonFatal() {
nonFatal = true;
return this;
}
public boolean shouldBeReport() {
if (!stackTrace.contains("org.jackhuang"))
return false;
@@ -48,23 +34,6 @@ public class CrashReport {
return true;
}
public Map<String, Object> getMetrics(long runningTime) {
return mapOf(
pair("_run", runningTime),
pair("_app_version", Metadata.VERSION),
pair("_os", OperatingSystem.SYSTEM_NAME),
pair("_os_version", OperatingSystem.SYSTEM_VERSION),
pair("_disk_current", getDiskAvailable()),
pair("_disk_total", getDiskTotal()),
pair("_ram_current", getMemoryAvailable()),
pair("_ram_total", Runtime.getRuntime().maxMemory() / BYTES_IN_MB),
pair("_error", stackTrace),
pair("_logs", LOG.getLogs()),
pair("_name", throwable.getLocalizedMessage()),
pair("_nonfatal", nonFatal)
);
}
public String getDisplayText() {
return "---- Hello Minecraft! Crash Report ----\n" +
" Version: " + Metadata.VERSION + "\n" +
@@ -82,29 +51,4 @@ public class CrashReport {
" JVM Total Memory: " + Runtime.getRuntime().totalMemory() + "\n" +
" JVM Free Memory: " + Runtime.getRuntime().freeMemory() + "\n";
}
private static final Long BYTES_IN_MB = 1024L * 1024;
private static long getMemoryAvailable() {
Long total = Runtime.getRuntime().totalMemory();
Long availMem = Runtime.getRuntime().freeMemory();
return (total - availMem) / BYTES_IN_MB;
}
private static long getDiskAvailable() {
long total = 0, free = 0;
for (File f : File.listRoots()) {
total += f.getTotalSpace();
free += f.getUsableSpace();
}
return (total - free) / BYTES_IN_MB;
}
private static long getDiskTotal() {
long total = 0;
for (File f : File.listRoots()) {
total += f.getTotalSpace();
}
return total / BYTES_IN_MB;
}
}
@@ -1,38 +0,0 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.game;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public enum LoadingState {
DEPENDENCIES("launch.state.dependencies"),
MODS("launch.state.modpack"),
LOGGING_IN("launch.state.logging_in"),
LAUNCHING("launch.state.waiting_launching"),
DONE("launch.state.done");
private final String key;
LoadingState(String key) {
this.key = key;
}
public String getLocalizedMessage() {
return i18n(key);
}
}
@@ -59,15 +59,11 @@ import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.io.NetworkUtils;
import org.jetbrains.annotations.NotNull;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.*;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -297,25 +293,65 @@ class ModListPageSkin extends SkinBase<ModListPage> {
titleContainer.setSpacing(8);
ImageView imageView = new ImageView();
if (StringUtils.isNotBlank(modInfo.getModInfo().getLogoPath())) {
Task.supplyAsync(() -> {
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modInfo.getModInfo().getFile())) {
Path iconPath = fs.getPath(modInfo.getModInfo().getLogoPath());
Task.supplyAsync(() -> {
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modInfo.getModInfo().getFile())) {
String logoPath = modInfo.getModInfo().getLogoPath();
if (StringUtils.isNotBlank(logoPath)) {
Path iconPath = fs.getPath(logoPath);
if (Files.exists(iconPath)) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
Files.copy(iconPath, stream);
return new ByteArrayInputStream(stream.toByteArray());
try (InputStream stream = Files.newInputStream(iconPath)) {
Image image = new Image(stream, 40, 40, true, true);
if (!image.isError() && image.getWidth() == image.getHeight())
return image;
} catch (Throwable e) {
LOG.warning("Failed to load image " + logoPath, e);
}
}
}
return null;
}).whenComplete(Schedulers.javafx(), (stream, exception) -> {
if (stream != null) {
imageView.setImage(new Image(stream, 40, 40, true, true));
} else {
imageView.setImage(FXUtils.newBuiltinImage("/assets/img/command.png", 40, 40, true, true));
List<String> defaultPaths = new ArrayList<>(Arrays.asList(
"icon.png",
"logo.png",
"mod_logo.png",
"pack.png",
"logoFile.png"
));
String id = modInfo.getModInfo().getId();
if (StringUtils.isNotBlank(id)) {
defaultPaths.addAll(Arrays.asList(
"assets/" + id + "/icon.png",
"assets/" + id.replace("-", "") + "/icon.png",
id + ".png",
id + "-logo.png",
id + "-icon.png",
id + "_logo.png",
id + "_icon.png"
));
}
}).start();
}
for (String path : defaultPaths) {
Path iconPath = fs.getPath(path);
if (Files.exists(iconPath)) {
try (InputStream stream = Files.newInputStream(iconPath)) {
Image image = new Image(stream, 40, 40, true, true);
if (!image.isError() && image.getWidth() == image.getHeight())
return image;
}
}
}
} catch (Exception e) {
LOG.warning("Failed to load icon", e);
}
return null;
}).whenComplete(Schedulers.javafx(), (image, exception) -> {
if (image != null) {
imageView.setImage(image);
} else {
imageView.setImage(FXUtils.newBuiltinImage("/assets/img/command.png", 40, 40, true, true));
}
}).start();
TwoLineListItem title = new TwoLineListItem();
title.setTitle(modInfo.getModInfo().getName());
@@ -211,6 +211,7 @@ curse.category.4482=Extra Large
curse.category.4472=Tech
curse.category.4473=Magic
curse.category.5128=Vanilla+
curse.category.7418=Horror
# https://addons-ecs.forgesvc.net/api/v2/category/section/6
curse.category.5299=Education
@@ -187,6 +187,7 @@ curse.category.4482=Extra grande
curse.category.4472=Tecnología
curse.category.4473=Magia
curse.category.5128=Vanilla+
curse.category.7418=Horror
# https://addons-ecs.forgesvc.net/api/v2/category/section/6
curse.category.5299=Educación
@@ -173,6 +173,7 @@ curse.category.4479=ハードコア
curse.category.4482=特大
curse.category.4472=Tech
curse.category.4473=魔法
curse.category.7418=ホラー
# https://addons-ecs.forgesvc.net/api/v2/category/section/6
curse.category.5129=Vanilla+
@@ -184,6 +184,7 @@ curse.category.4482=Extra Large
curse.category.4472=Tech
curse.category.4473=Magic
curse.category.5128=Vanilla+
curse.category.7418=Horror
# https://addons-ecs.forgesvc.net/api/v2/category/section/6
curse.category.5299=Education
@@ -221,6 +221,7 @@ curse.category.4482=大型模組包
curse.category.4472=科技
curse.category.4473=魔法
curse.category.5128=原版增強
curse.category.7418=恐怖
# https://addons-ecs.forgesvc.net/api/v2/category/section/6
curse.category.5299=教育
@@ -222,6 +222,7 @@ curse.category.4482=大型整合包
curse.category.4472=科技
curse.category.4473=魔法
curse.category.5128=原版增强
curse.category.7418=恐怖
# https://addons-ecs.forgesvc.net/api/v2/category/section/6
curse.category.5299=教育
@@ -19,7 +19,7 @@ package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.download.fabric.FabricAPIVersionList;
import org.jackhuang.hmcl.download.fabric.FabricVersionList;
import org.jackhuang.hmcl.download.forge.ForgeBMCLVersionList;
import org.jackhuang.hmcl.download.forge.ForgeVersionList;
import org.jackhuang.hmcl.download.game.GameVersionList;
import org.jackhuang.hmcl.download.liteloader.LiteLoaderVersionList;
import org.jackhuang.hmcl.download.neoforge.NeoForgeOfficialVersionList;
@@ -35,7 +35,7 @@ public class MojangDownloadProvider implements DownloadProvider {
private final GameVersionList game;
private final FabricVersionList fabric;
private final FabricAPIVersionList fabricApi;
private final ForgeBMCLVersionList forge;
private final ForgeVersionList forge;
private final NeoForgeOfficialVersionList neoforge;
private final LiteLoaderVersionList liteLoader;
private final OptiFineBMCLVersionList optifine;
@@ -49,7 +49,7 @@ public class MojangDownloadProvider implements DownloadProvider {
this.game = new GameVersionList(this);
this.fabric = new FabricVersionList(this);
this.fabricApi = new FabricAPIVersionList(this);
this.forge = new ForgeBMCLVersionList(apiRoot);
this.forge = new ForgeVersionList(this);
this.neoforge = new NeoForgeOfficialVersionList(this);
this.liteLoader = new LiteLoaderVersionList(this);
this.optifine = new OptiFineBMCLVersionList(apiRoot);
@@ -43,9 +43,17 @@ public final class ForgeVersionList extends VersionList<ForgeRemoteVersion> {
return false;
}
private static String toLookupVersion(String gameVersion) {
return "1.7.10-pre4".equals(gameVersion) ? "1.7.10_pre4" : gameVersion;
}
private static String fromLookupVersion(String lookupVersion) {
return "1.7.10_pre4".equals(lookupVersion) ? "1.7.10-pre4" : lookupVersion;
}
@Override
public CompletableFuture<?> refreshAsync() {
return HttpRequest.GET(downloadProvider.injectURL(FORGE_LIST)).getJsonAsync(ForgeVersionRoot.class)
return HttpRequest.GET(FORGE_LIST).getJsonAsync(ForgeVersionRoot.class)
.thenAcceptAsync(root -> {
lock.writeLock().lock();
@@ -55,7 +63,7 @@ public final class ForgeVersionList extends VersionList<ForgeRemoteVersion> {
versions.clear();
for (Map.Entry<String, int[]> entry : root.getGameVersions().entrySet()) {
String gameVersion = VersionNumber.normalize(entry.getKey());
String gameVersion = fromLookupVersion(VersionNumber.normalize(entry.getKey()));
for (int v : entry.getValue()) {
ForgeVersion version = root.getNumber().get(v);
if (version == null)
@@ -72,7 +80,7 @@ public final class ForgeVersionList extends VersionList<ForgeRemoteVersion> {
if (jar == null)
continue;
versions.put(gameVersion, new ForgeRemoteVersion(
version.getGameVersion(), version.getVersion(), null, Collections.singletonList(jar)
toLookupVersion(version.getGameVersion()), version.getVersion(), null, Collections.singletonList(jar)
));
}
}
@@ -82,5 +90,5 @@ public final class ForgeVersionList extends VersionList<ForgeRemoteVersion> {
});
}
public static final String FORGE_LIST = "https://files.minecraftforge.net/maven/net/minecraftforge/forge/json";
public static final String FORGE_LIST = "https://hmcl-dev.github.io/metadata/forge/";
}
@@ -35,7 +35,6 @@ import java.util.stream.StreamSupport;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import static org.jackhuang.hmcl.util.Lang.tryCast;
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
/**
@@ -48,8 +47,11 @@ final class GameVersion {
private static Optional<String> getVersionFromJson(InputStream versionJson) {
try {
Map<?, ?> version = JsonUtils.fromNonNullJsonFully(versionJson, Map.class);
return tryCast(version.get("id"), String.class);
} catch (IOException | JsonParseException e) {
String id = (String) version.get("id");
if (id != null && id.contains(" / "))
id = id.substring(0, id.indexOf(" / "));
return Optional.ofNullable(id);
} catch (IOException | JsonParseException | ClassCastException e) {
LOG.warning("Failed to parse version.json", e);
return Optional.empty();
}