mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-09-24 03:33:46 -04:00
Feature: Verify intergrity before lauching terracotta.
This commit is contained in:
parent
d48cfc378e
commit
aead89da3a
@ -91,7 +91,11 @@ public class TerracottaControllerPage extends StackPane {
|
||||
holder.add(FXUtils.onWeakChangeAndOperate(UI_STATE, state -> {
|
||||
progressProperty.unbind();
|
||||
|
||||
if (state instanceof TerracottaState.Uninitialized) {
|
||||
if (state instanceof TerracottaState.Bootstrap) {
|
||||
statusProperty.set(i18n("terracotta.status.bootstrap"));
|
||||
progressProperty.set(-1);
|
||||
nodesProperty.setAll();
|
||||
} else if (state instanceof TerracottaState.Uninitialized) {
|
||||
statusProperty.set(i18n("terracotta.status.uninitialized"));
|
||||
progressProperty.set(0);
|
||||
|
||||
|
@ -0,0 +1,47 @@
|
||||
package org.jackhuang.hmcl.ui.terracotta.core;
|
||||
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.util.DigestUtils;
|
||||
import org.jackhuang.hmcl.util.Hex;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
public final class TerracottaDaemon {
|
||||
private final List<URI> links;
|
||||
private final FileDownloadTask.IntegrityCheck checking;
|
||||
private final Path path;
|
||||
|
||||
public TerracottaDaemon(List<URI> links, String classifier, FileDownloadTask.IntegrityCheck checking) {
|
||||
this.links = links;
|
||||
this.checking = checking;
|
||||
this.path = Metadata.DEPENDENCIES_DIRECTORY.resolve(
|
||||
String.format("terracota/%s/terracotta-%s", TerracottaMetadata.VERSION, classifier)
|
||||
).toAbsolutePath();
|
||||
}
|
||||
|
||||
public Path getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public FileDownloadTask create() {
|
||||
return new FileDownloadTask(links, path, checking);
|
||||
}
|
||||
|
||||
public boolean exists() throws IOException {
|
||||
if (!Files.exists(path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String checksum;
|
||||
try (InputStream is = Files.newInputStream(path)) {
|
||||
checksum = Hex.encodeHex(DigestUtils.digest(checking.getAlgorithm(), is));
|
||||
}
|
||||
return checksum.equalsIgnoreCase(checking.getChecksum());
|
||||
}
|
||||
}
|
@ -30,24 +30,29 @@ public final class TerracottaManager {
|
||||
private TerracottaManager() {
|
||||
}
|
||||
|
||||
private static final AtomicReference<TerracottaState> STATE_V = new AtomicReference<>();
|
||||
|
||||
static {
|
||||
if (TerracottaMetadata.PROVIDER == null) {
|
||||
STATE_V.setPlain(TerracottaState.Fatal.INSTANCE);
|
||||
} else if (TerracottaMetadata.PROVIDER.exist()) {
|
||||
TerracottaState.Launching launching = new TerracottaState.Launching();
|
||||
STATE_V.setPlain(launching);
|
||||
|
||||
launch(launching);
|
||||
} else {
|
||||
STATE_V.setPlain(TerracottaState.Uninitialized.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
private static final AtomicReference<TerracottaState> STATE_V = new AtomicReference<>(TerracottaState.Bootstrap.INSTANCE);
|
||||
private static final ReadOnlyObjectWrapper<TerracottaState> STATE = new ReadOnlyObjectWrapper<>(STATE_V.getPlain());
|
||||
private static final InvocationDispatcher<TerracottaState> STATE_D = InvocationDispatcher.runOn(Platform::runLater, STATE::set);
|
||||
|
||||
static {
|
||||
Task.runAsync(() -> {
|
||||
if (TerracottaMetadata.PROVIDER == null) {
|
||||
setState(TerracottaState.Fatal.INSTANCE);
|
||||
LOG.warning("Terracotta hasn't support your OS: " + org.jackhuang.hmcl.util.platform.Platform.SYSTEM_PLATFORM);
|
||||
} else if (TerracottaMetadata.PROVIDER.exists()) {
|
||||
TerracottaState.Launching launching = new TerracottaState.Launching();
|
||||
setState(launching);
|
||||
launch(launching);
|
||||
} else {
|
||||
setState(TerracottaState.Uninitialized.INSTANCE);
|
||||
}
|
||||
}).whenComplete(exception -> {
|
||||
if (exception != null) {
|
||||
compareAndSet(TerracottaState.Bootstrap.INSTANCE, TerracottaState.Fatal.INSTANCE);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public static ReadOnlyObjectProperty<TerracottaState> stateProperty() {
|
||||
return STATE.getReadOnlyProperty();
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.jackhuang.hmcl.ui.terracotta.core;
|
||||
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.ui.terracotta.core.provider.GeneralProvider;
|
||||
import org.jackhuang.hmcl.ui.terracotta.core.provider.ITerracottaProvider;
|
||||
import org.jackhuang.hmcl.ui.terracotta.core.provider.MacOSProvider;
|
||||
@ -11,21 +12,41 @@ public final class TerracottaMetadata {
|
||||
private TerracottaMetadata() {
|
||||
}
|
||||
|
||||
public static final TerracottaDaemon WINDOWS_X86_64 = create(
|
||||
"windows-x86_64.exe", "b1badefb1e503d4e9b886edab1bf3fb6b1ff75763b29a06fe7cc2f2343610d02"
|
||||
);
|
||||
public static final TerracottaDaemon WINDOWS_ARM64 = create(
|
||||
"windows-arm64.exe", "05f376bcf3a8317a36fd51b6335ad8e6821af03af78a90cc1b0ff91771e095f3"
|
||||
);
|
||||
|
||||
public static final TerracottaDaemon LINUX_X86_64 = create(
|
||||
"linux-x86_64", "ca197ab3780834a58e51d17fa57157f82486bc6b22bf57242eca169c6e408ede"
|
||||
);
|
||||
public static final TerracottaDaemon LINUX_ARM64 = create(
|
||||
"linux-arm64", "85949ef696668f0a6c08944c998342bc1bbad62f112d6c2663acc2a0cc3e1b3c"
|
||||
);
|
||||
|
||||
public static final TerracottaDaemon MACOS_INSTALLER_X86_64 = create(
|
||||
"macos-x86_64.pkg", "e46c71f0c446f9ba0bd67f7216b64bad811417a00e54d4841eb1c71e7f70f189"
|
||||
);
|
||||
public static final TerracottaDaemon MACOS_INSTALLER_ARM64 = create(
|
||||
"macos-arm64.pkg", "223ab9964c05867bd76fd66b0bc9dde18f3c2958356c9c15be8205dcb7bdee00"
|
||||
);
|
||||
public static final TerracottaDaemon MACOS_BIN_X86_64 = create(
|
||||
"macos-x86_64", "49b4813538e1c6c495d69760a289bd8d4bd3a7ef51cc4a7db7a6a33f45846440");
|
||||
public static final TerracottaDaemon MACOS_BIN_ARM64 = create(
|
||||
"macos-arm64", "9e4da85595301fec392a4efa7aff44f05c3c81666d99a0c9df5d1c368617dfff"
|
||||
);
|
||||
|
||||
public static final String VERSION = "0.3.8-rc.1";
|
||||
|
||||
public static final List<URI> WINDOWS_X86_64 = create(String.format("https://github.com/burningtnt/Terracotta/releases/download/V%1$s/terracotta-%1$s-windows-x86_64.exe", VERSION));
|
||||
public static final List<URI> WINDOWS_ARM64 = create(String.format("https://github.com/burningtnt/Terracotta/releases/download/V%1$s/terracotta-%1$s-windows-arm64.exe", VERSION));
|
||||
private static TerracottaDaemon create(String classifier, String hash) {
|
||||
String link = String.format("https://github.com/burningtnt/Terracotta/releases/download/V%1$s/terracotta-%1$s-%2$s", VERSION, classifier);
|
||||
|
||||
public static final List<URI> LINUX_X86_64 = create(String.format("https://github.com/burningtnt/Terracotta/releases/download/V%1$s/terracotta-%1$s-linux-x86_64", VERSION));
|
||||
public static final List<URI> LINUX_ARM64 = create(String.format("https://github.com/burningtnt/Terracotta/releases/download/V%1$s/terracotta-%1$s-linux-arm64", VERSION));
|
||||
|
||||
public static final List<URI> MACOS_INSTALLER_X86_64 = create(String.format("https://github.com/burningtnt/Terracotta/releases/download/V%1$s/terracotta-%1$s-macos-x86_64.pkg", VERSION));
|
||||
public static final List<URI> MACOS_INSTALLER_ARM64 = create(String.format("https://github.com/burningtnt/Terracotta/releases/download/V%1$s/terracotta-%1$s-macos-arm64.pkg", VERSION));
|
||||
public static final List<URI> MACOS_BIN_X86_64 = create(String.format("https://github.com/burningtnt/Terracotta/releases/download/V%1$s/terracotta-%1$s-macos-x86_64", VERSION));
|
||||
public static final List<URI> MACOS_BIN_ARM64 = create(String.format("https://github.com/burningtnt/Terracotta/releases/download/V%1$s/terracotta-%1$s-macos-arm64", VERSION));
|
||||
|
||||
private static List<URI> create(String s) {
|
||||
return List.of(URI.create("https://ghfast.top/" + s), URI.create("https://cdn.crashmc.com/" + s), URI.create(s));
|
||||
return new TerracottaDaemon(
|
||||
List.of(URI.create("https://ghfast.top/" + link), URI.create("https://cdn.crashmc.com/" + link), URI.create(link)),
|
||||
classifier, new FileDownloadTask.IntegrityCheck("SHA-256", hash)
|
||||
);
|
||||
}
|
||||
|
||||
public static final ITerracottaProvider PROVIDER = locateProvider();
|
||||
@ -39,9 +60,4 @@ public final class TerracottaMetadata {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getFileName(URI uri) {
|
||||
String p = uri.getPath();
|
||||
return p.substring(p.lastIndexOf('/') + 1);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,13 @@ public abstract class TerracottaState {
|
||||
protected TerracottaState() {
|
||||
}
|
||||
|
||||
public static final class Bootstrap extends TerracottaState {
|
||||
static final Bootstrap INSTANCE = new Bootstrap();
|
||||
|
||||
private Bootstrap() {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Uninitialized extends TerracottaState {
|
||||
static final Uninitialized INSTANCE = new Uninitialized();
|
||||
|
||||
|
@ -1,49 +1,40 @@
|
||||
package org.jackhuang.hmcl.ui.terracotta.core.provider;
|
||||
|
||||
import javafx.beans.property.DoubleProperty;
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.terracotta.core.TerracottaDaemon;
|
||||
import org.jackhuang.hmcl.ui.terracotta.core.TerracottaMetadata;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import org.jackhuang.hmcl.util.platform.Platform;
|
||||
|
||||
import java.net.URI;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public final class GeneralProvider implements ITerracottaProvider {
|
||||
public static final List<URI> TARGET = Map.of(
|
||||
public static final TerracottaDaemon TARGET = Map.of(
|
||||
Platform.WINDOWS_X86_64, TerracottaMetadata.WINDOWS_X86_64,
|
||||
Platform.WINDOWS_ARM64, TerracottaMetadata.WINDOWS_ARM64,
|
||||
Platform.LINUX_X86_64, TerracottaMetadata.LINUX_X86_64,
|
||||
Platform.LINUX_ARM64, TerracottaMetadata.LINUX_ARM64
|
||||
).get(Platform.SYSTEM_PLATFORM);
|
||||
|
||||
private static final Path PATH = TARGET != null ? Metadata.DEPENDENCIES_DIRECTORY.resolve(String.format(
|
||||
"terracota/%s/%s", TerracottaMetadata.VERSION, TerracottaMetadata.getFileName(TARGET.get(0))
|
||||
)).toAbsolutePath() : null;
|
||||
|
||||
@Override
|
||||
public boolean exist() {
|
||||
return Files.exists(PATH);
|
||||
public boolean exists() throws IOException {
|
||||
return TARGET.exists();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<?> install(DoubleProperty progress) {
|
||||
Path tmp = PATH.resolveSibling(PATH.getFileName() + ".tmp");
|
||||
|
||||
Task<?> task = new FileDownloadTask(TARGET, tmp);
|
||||
Task<?> task = TARGET.create();
|
||||
progress.bind(task.progressProperty());
|
||||
task = task.thenRunAsync(() -> Files.move(tmp, PATH, StandardCopyOption.REPLACE_EXISTING));
|
||||
|
||||
if (OperatingSystem.CURRENT_OS.isLinuxOrBSD()) {
|
||||
task = task.thenRunAsync(() -> Files.setPosixFilePermissions(PATH, Set.of(
|
||||
task = task.thenRunAsync(() -> Files.setPosixFilePermissions(TARGET.getPath(), Set.of(
|
||||
PosixFilePermission.OWNER_READ,
|
||||
PosixFilePermission.OWNER_WRITE,
|
||||
PosixFilePermission.OWNER_EXECUTE,
|
||||
@ -58,6 +49,6 @@ public final class GeneralProvider implements ITerracottaProvider {
|
||||
|
||||
@Override
|
||||
public List<String> launch(Path path) {
|
||||
return List.of(PATH.toString(), "--hmcl", path.toString());
|
||||
return List.of(TARGET.getPath().toString(), "--hmcl", path.toString());
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
public interface ITerracottaProvider {
|
||||
boolean exist();
|
||||
boolean exists() throws IOException;
|
||||
|
||||
Task<?> install(DoubleProperty progress) throws IOException;
|
||||
|
||||
|
@ -1,19 +1,16 @@
|
||||
package org.jackhuang.hmcl.ui.terracotta.core.provider;
|
||||
|
||||
import javafx.beans.property.DoubleProperty;
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.terracotta.core.TerracottaDaemon;
|
||||
import org.jackhuang.hmcl.ui.terracotta.core.TerracottaMetadata;
|
||||
import org.jackhuang.hmcl.util.platform.Architecture;
|
||||
import org.jackhuang.hmcl.util.platform.ManagedProcess;
|
||||
import org.jackhuang.hmcl.util.platform.SystemUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@ -21,7 +18,7 @@ import java.util.Set;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public final class MacOSProvider implements ITerracottaProvider {
|
||||
public static final List<URI> INSTALLER, BINARY;
|
||||
public static final TerracottaDaemon INSTALLER, BINARY;
|
||||
|
||||
static {
|
||||
if (Architecture.SYSTEM_ARCH == Architecture.X86_64) {
|
||||
@ -36,24 +33,20 @@ public final class MacOSProvider implements ITerracottaProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private static final Path PATH = BINARY != null ? Metadata.DEPENDENCIES_DIRECTORY.resolve(String.format(
|
||||
"terracota/%s/%s", TerracottaMetadata.VERSION, TerracottaMetadata.getFileName(BINARY.get(0))
|
||||
)).toAbsolutePath() : null;
|
||||
|
||||
@Override
|
||||
public boolean exist() {
|
||||
return Files.exists(Path.of("/Applications/terracotta.app")) && Files.exists(PATH);
|
||||
public boolean exists() throws IOException {
|
||||
assert BINARY != null;
|
||||
|
||||
return Files.exists(Path.of("/Applications/terracotta.app")) && BINARY.exists();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<?> install(DoubleProperty progress) throws IOException {
|
||||
Path installer = Files.createTempFile("hmcl-terracotta-installer-", ".pkg").toAbsolutePath();
|
||||
Task<?> installerTask = new FileDownloadTask(INSTALLER, installer);
|
||||
assert INSTALLER != null && BINARY != null;
|
||||
|
||||
Path binary = PATH.resolveSibling(PATH.getFileName() + ".tmp");
|
||||
Task<?> binaryTask = new FileDownloadTask(BINARY, binary);
|
||||
|
||||
progress.bind(installerTask.progressProperty().add(binaryTask.progressProperty()).multiply(0.3));
|
||||
Task<?> installerTask = INSTALLER.create();
|
||||
Task<?> binaryTask = BINARY.create();
|
||||
progress.bind(installerTask.progressProperty().add(binaryTask.progressProperty()).multiply(0.4)); // (1 + 1) * 0.4 = 0.8
|
||||
|
||||
installerTask = installerTask.thenComposeAsync(() -> {
|
||||
ManagedProcess process = new ManagedProcess(new ProcessBuilder(
|
||||
@ -61,7 +54,7 @@ public final class MacOSProvider implements ITerracottaProvider {
|
||||
"-e",
|
||||
String.format(
|
||||
"do shell script \"%s\" with prompt \"%s\" with administrator privileges",
|
||||
String.format("installer -pkg %s -target /Applications", installer),
|
||||
String.format("installer -pkg %s -target /Applications", INSTALLER.getPath()),
|
||||
i18n("terracotta.sudo_installing")
|
||||
)
|
||||
));
|
||||
@ -71,8 +64,7 @@ public final class MacOSProvider implements ITerracottaProvider {
|
||||
return Task.fromCompletableFuture(process.getProcess().onExit());
|
||||
});
|
||||
binaryTask = binaryTask.thenRunAsync(() -> {
|
||||
Files.move(binary, PATH, StandardCopyOption.REPLACE_EXISTING);
|
||||
Files.setPosixFilePermissions(PATH, Set.of(
|
||||
Files.setPosixFilePermissions(BINARY.getPath(), Set.of(
|
||||
PosixFilePermission.OWNER_READ,
|
||||
PosixFilePermission.OWNER_WRITE,
|
||||
PosixFilePermission.OWNER_EXECUTE,
|
||||
@ -88,6 +80,8 @@ public final class MacOSProvider implements ITerracottaProvider {
|
||||
|
||||
@Override
|
||||
public List<String> launch(Path path) {
|
||||
return List.of(PATH.toString(), "--hmcl", path.toString());
|
||||
assert BINARY != null;
|
||||
|
||||
return List.of(BINARY.getPath().toString(), "--hmcl", path.toString());
|
||||
}
|
||||
}
|
||||
|
@ -1197,6 +1197,7 @@ terracotta.status=联机大厅
|
||||
terracotta.back=退出
|
||||
terracotta.network_warning=多人联机基于 p2p,最终联机体验和您的网络情况有较大关系。
|
||||
terracotta.sudo_installing=HMCL 需要验证您的密码才能安装联机核心
|
||||
terracotta.status.bootstrap=正在收集信息
|
||||
terracotta.status.uninitialized=未下载联机核心
|
||||
terracotta.status.uninitialized.title=下载联机核心(约 8MB)
|
||||
terracotta.status.uninitialized.desc=您承诺,在多人联机全过程中,您将严格遵守您所在国家或地区的全部法律法规
|
||||
|
Loading…
x
Reference in New Issue
Block a user