Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0cee859e15 | ||
|
|
7b987c9b95 | ||
|
|
8cdd8b3f70 | ||
|
|
956b4e4a6a | ||
|
|
4e0ffc8d1e | ||
|
|
177935241f | ||
|
|
f133fcf66c | ||
|
|
869f57cbf1 | ||
|
|
de7c2e4f07 | ||
|
|
8bc2650f7b | ||
|
|
cef9f0b2bb | ||
|
|
e36b28c957 | ||
|
|
a770a7ee5c | ||
|
|
e3e9a19f6f | ||
|
|
4772a144ae | ||
|
|
d32ba75d5f | ||
|
|
96f869278b | ||
|
|
df1f9c76ad | ||
|
|
0b934bf6b5 | ||
|
|
f5319fa94c | ||
|
|
28229471f4 | ||
|
|
d00c61b89f | ||
|
|
1f7d3591b9 | ||
|
|
4e537de894 | ||
|
|
e130a14ebe | ||
|
|
31aa2f3a27 |
No files matched your search
@@ -20,9 +20,6 @@ package org.jackhuang.hmcl;
|
||||
import javafx.application.Application;
|
||||
import javafx.application.Platform;
|
||||
import javafx.stage.Stage;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
|
||||
import org.jackhuang.hmcl.setting.Accounts;
|
||||
import org.jackhuang.hmcl.setting.AuthlibInjectorServers;
|
||||
import org.jackhuang.hmcl.setting.ConfigHolder;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.TaskExecutor;
|
||||
@@ -44,7 +41,6 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
@@ -58,14 +54,6 @@ public final class Launcher extends Application {
|
||||
try {
|
||||
try {
|
||||
ConfigHolder.init();
|
||||
AuthlibInjectorServers.init();
|
||||
|
||||
if (ConfigHolder.isNewlyCreated() && !AuthlibInjectorServers.getConfigInstance().getUrls().isEmpty()) {
|
||||
config().setPreferredLoginType(Accounts.getLoginType(Accounts.FACTORY_AUTHLIB_INJECTOR));
|
||||
AuthlibInjectorServers.getConfigInstance().getUrls().stream()
|
||||
.map(AuthlibInjectorServer::new)
|
||||
.forEach(config().getAuthlibInjectorServers()::add);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Main.showErrorAndExit(i18n("fatal.config_loading_failure", Paths.get("").toAbsolutePath().normalize()));
|
||||
}
|
||||
|
||||
@@ -62,17 +62,7 @@ public class HMCLModpackExportTask extends Task<Void> {
|
||||
blackList.add(version + ".json");
|
||||
Logging.LOG.info("Compressing game files without some files in blacklist, including files or directories: usernamecache.json, asm, logs, backups, versions, assets, usercache.json, libraries, crash-reports, launcher_profiles.json, NVIDIA, TCNodeTracker");
|
||||
try (Zipper zip = new Zipper(output.toPath())) {
|
||||
zip.putDirectory(repository.getRunDirectory(version).toPath(), "minecraft", path -> {
|
||||
if (path.isEmpty())
|
||||
return true;
|
||||
for (String s : blackList)
|
||||
if (path.equals(s))
|
||||
return false;
|
||||
for (String s : whitelist)
|
||||
if (path.equals(s))
|
||||
return true;
|
||||
return false;
|
||||
});
|
||||
zip.putDirectory(repository.getRunDirectory(version).toPath(), "minecraft", path -> Modpack.acceptFile(path, blackList, whitelist));
|
||||
|
||||
Version mv = repository.getResolvedPreservingPatchesVersion(version);
|
||||
String gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version))
|
||||
|
||||
@@ -31,9 +31,10 @@ import org.jackhuang.hmcl.launch.NotDecompressingNativesException;
|
||||
import org.jackhuang.hmcl.launch.PermissionException;
|
||||
import org.jackhuang.hmcl.launch.ProcessCreationException;
|
||||
import org.jackhuang.hmcl.launch.ProcessListener;
|
||||
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
||||
import org.jackhuang.hmcl.mod.curse.CurseCompletionException;
|
||||
import org.jackhuang.hmcl.mod.curse.CurseCompletionTask;
|
||||
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
||||
import org.jackhuang.hmcl.mod.server.ServerModpackCompletionTask;
|
||||
import org.jackhuang.hmcl.setting.LauncherVisibility;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
import org.jackhuang.hmcl.setting.VersionSetting;
|
||||
@@ -62,13 +63,7 @@ import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Queue;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -147,6 +142,8 @@ public final class LauncherHelper {
|
||||
ModpackConfiguration<?> configuration = ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(selectedVersion));
|
||||
if ("Curse".equals(configuration.getType()))
|
||||
return new CurseCompletionTask(dependencyManager, selectedVersion);
|
||||
else if ("Server".equals(configuration.getType()))
|
||||
return new ServerModpackCompletionTask(dependencyManager, selectedVersion);
|
||||
else
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
|
||||
@@ -25,6 +25,8 @@ import org.jackhuang.hmcl.mod.curse.CurseInstallTask;
|
||||
import org.jackhuang.hmcl.mod.curse.CurseManifest;
|
||||
import org.jackhuang.hmcl.mod.multimc.MultiMCInstanceConfiguration;
|
||||
import org.jackhuang.hmcl.mod.multimc.MultiMCModpackInstallTask;
|
||||
import org.jackhuang.hmcl.mod.server.ServerModpackInstallTask;
|
||||
import org.jackhuang.hmcl.mod.server.ServerModpackManifest;
|
||||
import org.jackhuang.hmcl.setting.EnumGameDirectory;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
import org.jackhuang.hmcl.setting.VersionSetting;
|
||||
@@ -65,6 +67,12 @@ public final class ModpackHelper {
|
||||
// ignore it, not a valid MultiMC modpack.
|
||||
}
|
||||
|
||||
try {
|
||||
return ServerModpackManifest.readManifest(file, charset);
|
||||
} catch (Exception e) {
|
||||
// ignore it, not a valid Server modpack.
|
||||
}
|
||||
|
||||
throw new UnsupportedModpackException(file.toString());
|
||||
}
|
||||
|
||||
@@ -120,7 +128,10 @@ public final class ModpackHelper {
|
||||
return new MultiMCModpackInstallTask(profile.getDependency(), zipFile, modpack, ((MultiMCInstanceConfiguration) modpack.getManifest()), name)
|
||||
.whenComplete(Schedulers.defaultScheduler(), success, failure)
|
||||
.thenComposeAsync(new MultiMCInstallVersionSettingTask(profile, ((MultiMCInstanceConfiguration) modpack.getManifest()), name));
|
||||
else throw new IllegalArgumentException("Unrecognized modpack: " + modpack);
|
||||
else if (modpack.getManifest() instanceof ServerModpackManifest)
|
||||
return new ServerModpackInstallTask(profile.getDependency(), zipFile, modpack, ((ServerModpackManifest) modpack.getManifest()), name)
|
||||
.whenComplete(Schedulers.defaultScheduler(), success, failure);
|
||||
else throw new IllegalArgumentException("Unrecognized modpack: " + modpack.getManifest());
|
||||
}
|
||||
|
||||
public static Task<Void> getUpdateTask(Profile profile, File zipFile, Charset charset, String name, ModpackConfiguration<?> configuration) throws UnsupportedModpackException, MismatchedModpackTypeException {
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
package org.jackhuang.hmcl.setting;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.gson.Validation;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
@@ -30,6 +33,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
|
||||
public class AuthlibInjectorServers implements Validation {
|
||||
@@ -70,6 +74,15 @@ public class AuthlibInjectorServers implements Validation {
|
||||
LOG.log(Level.WARNING, "Malformed authlib-injectors.json", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (ConfigHolder.isNewlyCreated() && !AuthlibInjectorServers.getConfigInstance().getUrls().isEmpty()) {
|
||||
config().setPreferredLoginType(Accounts.getLoginType(Accounts.FACTORY_AUTHLIB_INJECTOR));
|
||||
for (String url : AuthlibInjectorServers.getConfigInstance().getUrls()) {
|
||||
Task.supplyAsync(Schedulers.io(), () -> AuthlibInjectorServer.locateServer(url))
|
||||
.thenAcceptAsync(Schedulers.javafx(), server -> config().getAuthlibInjectorServers().add(server))
|
||||
.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static AuthlibInjectorServers getConfigInstance() {
|
||||
|
||||
@@ -48,6 +48,7 @@ public class Settings {
|
||||
ProxyManager.init();
|
||||
Accounts.init();
|
||||
Profiles.init();
|
||||
AuthlibInjectorServers.init();
|
||||
|
||||
CacheRepository.setInstance(HMCLCacheRepository.REPOSITORY);
|
||||
HMCLCacheRepository.REPOSITORY.directoryProperty().bind(Bindings.createStringBinding(() -> {
|
||||
|
||||
@@ -17,12 +17,14 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui;
|
||||
|
||||
import com.jfoenix.controls.JFXCheckBox;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.ReadOnlyIntegerProperty;
|
||||
import javafx.beans.property.ReadOnlyIntegerWrapper;
|
||||
import javafx.concurrent.Worker;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.ToggleButton;
|
||||
import javafx.scene.layout.StackPane;
|
||||
@@ -122,7 +124,8 @@ public final class LogWindow extends Stage {
|
||||
pre.setTextContent(line);
|
||||
div.appendChild(pre);
|
||||
impl.body.appendChild(div);
|
||||
impl.engine.executeScript("checkNewLog(\"" + level.name().toLowerCase() + "\");scrollToBottom();");
|
||||
//impl.engine.executeScript("checkNewLog(\"" + level.name().toLowerCase() + "\");scrollToBottom();");
|
||||
impl.engine.executeScript("checkNewLog(\"" + level.name().toLowerCase() + "\");" + (impl.autoscroll.isSelected() ? "scrollToBottom();" : ""));
|
||||
|
||||
switch (level) {
|
||||
case FATAL:
|
||||
@@ -166,6 +169,8 @@ public final class LogWindow extends Stage {
|
||||
private ToggleButton btnDebugs;
|
||||
@FXML
|
||||
private ComboBox<String> cboLines;
|
||||
@FXML
|
||||
private CheckBox autoscroll;
|
||||
|
||||
final WebEngine engine;
|
||||
Node body;
|
||||
|
||||
@@ -122,6 +122,8 @@ public class ModpackInstallWizardProvider implements WizardProvider {
|
||||
public Node createPage(WizardController controller, int step, Map<String, Object> settings) {
|
||||
switch (step) {
|
||||
case 0:
|
||||
return new ModpackSelectionPage(controller);
|
||||
case 1:
|
||||
return new ModpackPage(controller);
|
||||
default:
|
||||
throw new IllegalStateException("error step " + step + ", settings: " + settings + ", pages: " + controller.getPages());
|
||||
|
||||
@@ -151,7 +151,7 @@ public final class ModpackPage extends StackPane implements WizardPage {
|
||||
if (manifest != null) {
|
||||
WebStage stage = new WebStage();
|
||||
stage.getWebView().getEngine().loadContent(manifest.getDescription());
|
||||
stage.setTitle(i18n("modpack.wizard.step.3"));
|
||||
stage.setTitle(i18n("modpack.description"));
|
||||
stage.showAndWait();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2019 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.ui.download;
|
||||
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import javafx.application.Platform;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.stage.FileChooser;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardPage;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.download.ModpackPage.MODPACK_FILE;
|
||||
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public final class ModpackSelectionPage extends StackPane implements WizardPage {
|
||||
private final WizardController controller;
|
||||
|
||||
@FXML private JFXButton btnLocal;
|
||||
@FXML private JFXButton btnRemote;
|
||||
|
||||
public ModpackSelectionPage(WizardController controller) {
|
||||
this.controller = controller;
|
||||
FXUtils.loadFXML(this, "/assets/fxml/download/modpack-source.fxml");
|
||||
|
||||
Optional<File> filePath = tryCast(controller.getSettings().get(MODPACK_FILE), File.class);
|
||||
if (filePath.isPresent()) {
|
||||
controller.getSettings().put(MODPACK_FILE, filePath.get());
|
||||
Platform.runLater(controller::onNext);
|
||||
}
|
||||
|
||||
FXUtils.applyDragListener(this, it -> "zip".equals(FileUtils.getExtension(it)), modpacks -> {
|
||||
File modpack = modpacks.get(0);
|
||||
controller.getSettings().put(MODPACK_FILE, modpack);
|
||||
controller.onNext();
|
||||
});
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onChooseLocalFile() {
|
||||
FileChooser chooser = new FileChooser();
|
||||
chooser.setTitle(i18n("modpack.choose"));
|
||||
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("modpack"), "*.zip"));
|
||||
File selectedFile = chooser.showOpenDialog(Controllers.getStage());
|
||||
if (selectedFile == null) {
|
||||
Platform.runLater(controller::onEnd);
|
||||
return;
|
||||
}
|
||||
|
||||
controller.getSettings().put(MODPACK_FILE, selectedFile);
|
||||
controller.onNext();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onChooseRemoteFile() {
|
||||
Controllers.inputDialog(i18n("modpack.choose.remote.tooltip"), (urlString, resolve, reject) -> {
|
||||
try {
|
||||
URL url = new URL(urlString);
|
||||
Path modpack = Files.createTempFile("forge-installer", ".jar");
|
||||
resolve.run();
|
||||
|
||||
Controllers.taskDialog(
|
||||
new FileDownloadTask(url, modpack.toFile(), null)
|
||||
.whenComplete(Schedulers.javafx(), e -> {
|
||||
if (e == null) {
|
||||
resolve.run();
|
||||
controller.getSettings().put(MODPACK_FILE, modpack.toFile());
|
||||
controller.onNext();
|
||||
} else {
|
||||
reject.accept(e.getMessage());
|
||||
}
|
||||
}).executor(true),
|
||||
i18n("message.downloading")
|
||||
);
|
||||
} catch (IOException e) {
|
||||
reject.accept(e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup(Map<String, Object> settings) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return i18n("modpack.task.install");
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import org.jackhuang.hmcl.mod.ModAdviser;
|
||||
import org.jackhuang.hmcl.mod.Modpack;
|
||||
import org.jackhuang.hmcl.mod.multimc.MultiMCInstanceConfiguration;
|
||||
import org.jackhuang.hmcl.mod.multimc.MultiMCModpackExportTask;
|
||||
import org.jackhuang.hmcl.mod.server.ServerModpackExportTask;
|
||||
import org.jackhuang.hmcl.setting.Config;
|
||||
import org.jackhuang.hmcl.setting.ConfigHolder;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
@@ -64,6 +65,7 @@ public final class ExportWizardProvider implements WizardProvider {
|
||||
File modpackFile = (File) settings.get(ModpackInfoPage.MODPACK_FILE);
|
||||
String modpackName = (String) settings.get(ModpackInfoPage.MODPACK_NAME);
|
||||
String modpackAuthor = (String) settings.get(ModpackInfoPage.MODPACK_AUTHOR);
|
||||
String modpackFileApi = (String) settings.get(ModpackInfoPage.MODPACK_FILE_API);
|
||||
String modpackVersion = (String) settings.get(ModpackInfoPage.MODPACK_VERSION);
|
||||
String modpackDescription = (String) settings.get(ModpackInfoPage.MODPACK_DESCRIPTION);
|
||||
String modpackType = (String) settings.get(ModpackTypeSelectionPage.MODPACK_TYPE);
|
||||
@@ -74,6 +76,8 @@ public final class ExportWizardProvider implements WizardProvider {
|
||||
return exportAsHMCL(whitelist, modpackFile, modpackName, modpackAuthor, modpackVersion, modpackDescription, includeLauncher);
|
||||
case ModpackTypeSelectionPage.MODPACK_TYPE_MULTIMC:
|
||||
return exportAsMultiMC(whitelist, modpackFile, modpackName, modpackAuthor, modpackVersion, modpackDescription);
|
||||
case ModpackTypeSelectionPage.MODPACK_TYPE_SERVER:
|
||||
return exportAsServer(whitelist, modpackFile, modpackName, modpackAuthor, modpackVersion, modpackDescription, modpackFileApi);
|
||||
default:
|
||||
throw new IllegalStateException("Unrecognized modpack type " + modpackType);
|
||||
}
|
||||
@@ -177,12 +181,27 @@ public final class ExportWizardProvider implements WizardProvider {
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
private Task<?> exportAsServer(List<String> whitelist, File modpackFile, String modpackName, String modpackAuthor, String modpackVersion, String modpackDescription, String modpackFileApi) {
|
||||
return new Task<Void>() {
|
||||
Task<?> dependency;
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
dependency = new ServerModpackExportTask(profile.getRepository(), version, whitelist, modpackName, modpackAuthor, modpackVersion, modpackDescription, modpackFileApi, modpackFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Task<?>> getDependencies() {
|
||||
return Collections.singleton(dependency);
|
||||
}
|
||||
};
|
||||
}
|
||||
@Override
|
||||
public Node createPage(WizardController controller, int step, Map<String, Object> settings) {
|
||||
switch (step) {
|
||||
case 0: return new ModpackInfoPage(controller, version);
|
||||
case 1: return new ModpackFileSelectionPage(controller, profile, version, ModAdviser::suggestMod);
|
||||
case 2: return new ModpackTypeSelectionPage(controller);
|
||||
case 0: return new ModpackTypeSelectionPage(controller);
|
||||
case 1: return new ModpackInfoPage(controller, version);
|
||||
case 2: return new ModpackFileSelectionPage(controller, profile, version, ModAdviser::suggestMod);
|
||||
default: throw new IllegalArgumentException("step");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ public final class ModpackFileSelectionPage extends StackPane implements WizardP
|
||||
LinkedList<String> list = new LinkedList<>();
|
||||
getFilesNeeded(rootNode, "minecraft", list);
|
||||
controller.getSettings().put(MODPACK_FILE_SELECTION, list);
|
||||
controller.onNext();
|
||||
controller.onFinish();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -156,6 +156,7 @@ public final class ModpackFileSelectionPage extends StackPane implements WizardP
|
||||
|
||||
public static final String MODPACK_FILE_SELECTION = "modpack.accepted";
|
||||
private static final Map<String, String> TRANSLATION = mapOf(
|
||||
pair("minecraft/hmclversion.cfg", i18n("modpack.files.hmclversion_cfg")),
|
||||
pair("minecraft/servers.dat", i18n("modpack.files.servers_dat")),
|
||||
pair("minecraft/saves", i18n("modpack.files.saves")),
|
||||
pair("minecraft/mods", i18n("modpack.files.mods")),
|
||||
|
||||
@@ -21,63 +21,69 @@ import com.jfoenix.controls.JFXButton;
|
||||
import com.jfoenix.controls.JFXTextArea;
|
||||
import com.jfoenix.controls.JFXTextField;
|
||||
import com.jfoenix.controls.JFXToggleButton;
|
||||
import com.jfoenix.validation.RequiredFieldValidator;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.text.TextAlignment;
|
||||
import javafx.stage.FileChooser;
|
||||
import org.jackhuang.hmcl.Launcher;
|
||||
import org.jackhuang.hmcl.auth.Account;
|
||||
import org.jackhuang.hmcl.setting.Accounts;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.construct.ComponentList;
|
||||
import org.jackhuang.hmcl.ui.construct.Validator;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardPage;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.export.ModpackTypeSelectionPage.*;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public final class ModpackInfoPage extends StackPane implements WizardPage {
|
||||
public final class ModpackInfoPage extends Control implements WizardPage {
|
||||
private final WizardController controller;
|
||||
@FXML
|
||||
private Label lblVersionName;
|
||||
@FXML
|
||||
private JFXTextField txtModpackName;
|
||||
@FXML
|
||||
private JFXTextField txtModpackAuthor;
|
||||
@FXML
|
||||
private JFXTextField txtModpackVersion;
|
||||
@FXML
|
||||
private JFXTextArea txtModpackDescription;
|
||||
@FXML
|
||||
private JFXToggleButton chkIncludeLauncher;
|
||||
@FXML
|
||||
private JFXButton btnNext;
|
||||
@FXML
|
||||
private ScrollPane scroll;
|
||||
private final boolean canIncludeLauncher;
|
||||
private final boolean showFileApi;
|
||||
|
||||
private SimpleStringProperty versionName = new SimpleStringProperty();
|
||||
private SimpleStringProperty modpackName = new SimpleStringProperty();
|
||||
private SimpleStringProperty modpackFileApi = new SimpleStringProperty();
|
||||
private SimpleStringProperty modpackAuthor = new SimpleStringProperty();
|
||||
private SimpleStringProperty modpackVersion = new SimpleStringProperty("1.0");
|
||||
private SimpleStringProperty modpackDescription = new SimpleStringProperty();
|
||||
private SimpleBooleanProperty includingLauncher = new SimpleBooleanProperty();
|
||||
private ObjectProperty<EventHandler<? super MouseEvent>> next = new SimpleObjectProperty<>();
|
||||
|
||||
public ModpackInfoPage(WizardController controller, String version) {
|
||||
this.controller = controller;
|
||||
FXUtils.loadFXML(this, "/assets/fxml/modpack/info.fxml");
|
||||
FXUtils.smoothScrolling(scroll);
|
||||
txtModpackName.setText(version);
|
||||
txtModpackName.textProperty().addListener(e -> checkValidation());
|
||||
txtModpackAuthor.textProperty().addListener(e -> checkValidation());
|
||||
txtModpackVersion.textProperty().addListener(e -> checkValidation());
|
||||
txtModpackAuthor.setText(Optional.ofNullable(Accounts.getSelectedAccount()).map(Account::getUsername).orElse(""));
|
||||
lblVersionName.setText(version);
|
||||
modpackName.set(version);
|
||||
modpackAuthor.set(Optional.ofNullable(Accounts.getSelectedAccount()).map(Account::getUsername).orElse(""));
|
||||
versionName.set(version);
|
||||
|
||||
List<File> launcherJar = Launcher.getCurrentJarFiles();
|
||||
if (launcherJar == null)
|
||||
chkIncludeLauncher.setDisable(true);
|
||||
}
|
||||
canIncludeLauncher = launcherJar != null;
|
||||
showFileApi = controller.getSettings().get(MODPACK_TYPE) == MODPACK_TYPE_SERVER;
|
||||
|
||||
private void checkValidation() {
|
||||
btnNext.setDisable(!txtModpackName.validate() || !txtModpackVersion.validate() || !txtModpackAuthor.validate());
|
||||
next.set(e -> onNext());
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -90,12 +96,13 @@ public final class ModpackInfoPage extends StackPane implements WizardPage {
|
||||
controller.onEnd();
|
||||
return;
|
||||
}
|
||||
controller.getSettings().put(MODPACK_NAME, txtModpackName.getText());
|
||||
controller.getSettings().put(MODPACK_VERSION, txtModpackVersion.getText());
|
||||
controller.getSettings().put(MODPACK_AUTHOR, txtModpackAuthor.getText());
|
||||
controller.getSettings().put(MODPACK_NAME, modpackName.get());
|
||||
controller.getSettings().put(MODPACK_FILE_API, modpackFileApi.get());
|
||||
controller.getSettings().put(MODPACK_VERSION, modpackVersion.get());
|
||||
controller.getSettings().put(MODPACK_AUTHOR, modpackAuthor.get());
|
||||
controller.getSettings().put(MODPACK_FILE, file);
|
||||
controller.getSettings().put(MODPACK_DESCRIPTION, txtModpackDescription.getText());
|
||||
controller.getSettings().put(MODPACK_INCLUDE_LAUNCHER, chkIncludeLauncher.isSelected());
|
||||
controller.getSettings().put(MODPACK_DESCRIPTION, modpackDescription.get());
|
||||
controller.getSettings().put(MODPACK_INCLUDE_LAUNCHER, includingLauncher.get());
|
||||
controller.onNext();
|
||||
}
|
||||
|
||||
@@ -114,10 +121,175 @@ public final class ModpackInfoPage extends StackPane implements WizardPage {
|
||||
return i18n("modpack.wizard.step.1.title");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Skin<?> createDefaultSkin() {
|
||||
return new ModpackInfoPageSkin(this);
|
||||
}
|
||||
|
||||
public static final String MODPACK_NAME = "modpack.name";
|
||||
public static final String MODPACK_FILE_API = "modpack.file_api";
|
||||
public static final String MODPACK_VERSION = "modpack.version";
|
||||
public static final String MODPACK_AUTHOR = "archive.author";
|
||||
public static final String MODPACK_DESCRIPTION = "modpack.description";
|
||||
public static final String MODPACK_INCLUDE_LAUNCHER = "modpack.include_launcher";
|
||||
public static final String MODPACK_FILE = "modpack.file";
|
||||
|
||||
public static class ModpackInfoPageSkin extends SkinBase<ModpackInfoPage> {
|
||||
private final JFXTextField txtModpackName;
|
||||
private final JFXTextField txtModpackFileApi;
|
||||
private final JFXTextField txtModpackAuthor;
|
||||
private final JFXTextField txtModpackVersion;
|
||||
|
||||
public ModpackInfoPageSkin(ModpackInfoPage skinnable) {
|
||||
super(skinnable);
|
||||
|
||||
Insets insets = new Insets(5, 0, 12, 0);
|
||||
Insets componentListMargin = new Insets(16, 0, 16, 0);
|
||||
|
||||
ScrollPane scroll = new ScrollPane();
|
||||
scroll.setFitToWidth(true);
|
||||
scroll.setFitToHeight(true);
|
||||
getChildren().setAll(scroll);
|
||||
|
||||
{
|
||||
BorderPane borderPane = new BorderPane();
|
||||
borderPane.setStyle("-fx-padding: 16;");
|
||||
scroll.setContent(borderPane);
|
||||
|
||||
if (skinnable.controller.getSettings().get(MODPACK_TYPE) == MODPACK_TYPE_SERVER) {
|
||||
Hyperlink hyperlink = new Hyperlink(i18n("modpack.wizard.step.initialization.server"));
|
||||
hyperlink.setOnMouseClicked(e -> {
|
||||
FXUtils.openLink("https://hmcl.huangyuhui.net/api/redirect/server-modpack");
|
||||
});
|
||||
borderPane.setTop(hyperlink);
|
||||
} else {
|
||||
Label label = new Label(i18n("modpack.wizard.step.initialization.warning"));
|
||||
label.setWrapText(true);
|
||||
label.setTextAlignment(TextAlignment.JUSTIFY);
|
||||
borderPane.setTop(label);
|
||||
}
|
||||
|
||||
{
|
||||
ComponentList list = new ComponentList();
|
||||
BorderPane.setMargin(list, componentListMargin);
|
||||
borderPane.setCenter(list);
|
||||
|
||||
{
|
||||
BorderPane borderPane1 = new BorderPane();
|
||||
borderPane1.setLeft(new Label(i18n("modpack.wizard.step.initialization.exported_version")));
|
||||
|
||||
Label versionNameLabel = new Label();
|
||||
versionNameLabel.textProperty().bind(skinnable.versionName);
|
||||
borderPane1.setRight(versionNameLabel);
|
||||
list.getContent().add(borderPane1);
|
||||
}
|
||||
|
||||
{
|
||||
txtModpackName = new JFXTextField();
|
||||
txtModpackName.textProperty().bindBidirectional(skinnable.modpackName);
|
||||
txtModpackName.setLabelFloat(true);
|
||||
txtModpackName.setPromptText(i18n("modpack.name"));
|
||||
RequiredFieldValidator validator = new RequiredFieldValidator();
|
||||
validator.setMessage(i18n("modpack.not_a_valid_name"));
|
||||
txtModpackName.getValidators().add(validator);
|
||||
StackPane.setMargin(txtModpackName, insets);
|
||||
list.getContent().add(txtModpackName);
|
||||
}
|
||||
|
||||
if (skinnable.showFileApi) {
|
||||
txtModpackFileApi = new JFXTextField();
|
||||
txtModpackFileApi.textProperty().bindBidirectional(skinnable.modpackFileApi);
|
||||
txtModpackFileApi.setLabelFloat(true);
|
||||
txtModpackFileApi.setPromptText(i18n("modpack.file_api"));
|
||||
RequiredFieldValidator validator = new RequiredFieldValidator();
|
||||
txtModpackFileApi.getValidators().add(validator);
|
||||
txtModpackFileApi.getValidators().add(new Validator(s -> {
|
||||
try {
|
||||
new URL(s).toURI();
|
||||
return true;
|
||||
} catch (IOException | URISyntaxException e) {
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
StackPane.setMargin(txtModpackFileApi, insets);
|
||||
list.getContent().add(txtModpackFileApi);
|
||||
} else {
|
||||
txtModpackFileApi = null;
|
||||
}
|
||||
|
||||
{
|
||||
txtModpackAuthor = new JFXTextField();
|
||||
txtModpackAuthor.textProperty().bindBidirectional(skinnable.modpackAuthor);
|
||||
txtModpackAuthor.setLabelFloat(true);
|
||||
txtModpackAuthor.setPromptText(i18n("archive.author"));
|
||||
RequiredFieldValidator validator = new RequiredFieldValidator();
|
||||
txtModpackAuthor.getValidators().add(validator);
|
||||
StackPane.setMargin(txtModpackAuthor, insets);
|
||||
list.getContent().add(txtModpackAuthor);
|
||||
}
|
||||
|
||||
{
|
||||
txtModpackVersion = new JFXTextField();
|
||||
txtModpackVersion.textProperty().bindBidirectional(skinnable.modpackVersion);
|
||||
txtModpackVersion.setLabelFloat(true);
|
||||
txtModpackVersion.setPromptText(i18n("archive.version"));
|
||||
RequiredFieldValidator validator = new RequiredFieldValidator();
|
||||
txtModpackVersion.getValidators().add(validator);
|
||||
StackPane.setMargin(txtModpackVersion, insets);
|
||||
list.getContent().add(txtModpackVersion);
|
||||
}
|
||||
|
||||
{
|
||||
JFXTextArea area = new JFXTextArea();
|
||||
area.textProperty().bindBidirectional(skinnable.modpackDescription);
|
||||
area.setLabelFloat(true);
|
||||
area.setPromptText(i18n("modpack.desc"));
|
||||
area.setMinHeight(400);
|
||||
StackPane.setMargin(area, insets);
|
||||
list.getContent().add(area);
|
||||
}
|
||||
|
||||
if (skinnable.controller.getSettings().get(MODPACK_TYPE) == MODPACK_TYPE_HMCL) {
|
||||
BorderPane borderPane1 = new BorderPane();
|
||||
borderPane1.setLeft(new Label(i18n("modpack.wizard.step.initialization.include_launcher")));
|
||||
list.getContent().add(borderPane1);
|
||||
|
||||
JFXToggleButton button = new JFXToggleButton();
|
||||
button.setDisable(!skinnable.canIncludeLauncher);
|
||||
button.selectedProperty().bindBidirectional(skinnable.includingLauncher);
|
||||
button.setSize(8);
|
||||
button.setMinHeight(16);
|
||||
button.setMaxHeight(16);
|
||||
borderPane1.setRight(button);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
HBox hbox = new HBox();
|
||||
hbox.setAlignment(Pos.CENTER_RIGHT);
|
||||
borderPane.setBottom(hbox);
|
||||
|
||||
JFXButton nextButton = new JFXButton();
|
||||
nextButton.onMouseClickedProperty().bind(skinnable.next);
|
||||
nextButton.setPrefWidth(100);
|
||||
nextButton.setPrefHeight(40);
|
||||
nextButton.setButtonType(JFXButton.ButtonType.RAISED);
|
||||
nextButton.setText(i18n("wizard.next"));
|
||||
nextButton.getStyleClass().add("jfx-button-raised");
|
||||
if (skinnable.showFileApi) {
|
||||
nextButton.disableProperty().bind(Bindings.createBooleanBinding(() ->
|
||||
!txtModpackName.validate() || !txtModpackVersion.validate() || !txtModpackAuthor.validate() || !txtModpackFileApi.validate(),
|
||||
txtModpackName.textProperty(), txtModpackAuthor.textProperty(), txtModpackVersion.textProperty(), txtModpackFileApi.textProperty()));
|
||||
} else {
|
||||
nextButton.disableProperty().bind(Bindings.createBooleanBinding(() ->
|
||||
!txtModpackName.validate() || !txtModpackVersion.validate() || !txtModpackAuthor.validate(),
|
||||
txtModpackName.textProperty(), txtModpackAuthor.textProperty(), txtModpackVersion.textProperty()));
|
||||
}
|
||||
hbox.getChildren().add(nextButton);
|
||||
}
|
||||
}
|
||||
|
||||
FXUtils.smoothScrolling(scroll);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,18 +34,20 @@ public final class ModpackTypeSelectionPage extends StackPane implements WizardP
|
||||
private JFXButton btnHMCL;
|
||||
@FXML
|
||||
private JFXButton btnMultiMC;
|
||||
@FXML
|
||||
private JFXButton btnServer;
|
||||
|
||||
public ModpackTypeSelectionPage(WizardController controller) {
|
||||
this.controller = controller;
|
||||
FXUtils.loadFXML(this, "/assets/fxml/modpack/type.fxml");
|
||||
|
||||
JFXButton[] buttons = new JFXButton[]{btnHMCL, btnMultiMC};
|
||||
String[] types = new String[]{MODPACK_TYPE_HMCL, MODPACK_TYPE_MULTIMC};
|
||||
JFXButton[] buttons = new JFXButton[]{btnHMCL, btnMultiMC, btnServer};
|
||||
String[] types = new String[]{MODPACK_TYPE_HMCL, MODPACK_TYPE_MULTIMC, MODPACK_TYPE_SERVER};
|
||||
for (int i = 0; i < types.length; ++i) {
|
||||
String type = types[i];
|
||||
buttons[i].setOnMouseClicked(e -> {
|
||||
controller.getSettings().put(MODPACK_TYPE, type);
|
||||
controller.onFinish();
|
||||
controller.onNext();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -61,6 +63,7 @@ public final class ModpackTypeSelectionPage extends StackPane implements WizardP
|
||||
|
||||
public static final String MODPACK_TYPE = "modpack.type";
|
||||
|
||||
public static final String MODPACK_TYPE_MULTIMC = "multimc";
|
||||
public static final String MODPACK_TYPE_HMCL = "hmcl";
|
||||
public static final String MODPACK_TYPE_MULTIMC = "multimc";
|
||||
public static final String MODPACK_TYPE_SERVER = "server";
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import com.jfoenix.controls.*?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import org.jackhuang.hmcl.ui.construct.TwoLineListItem?>
|
||||
<fx:root xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
type="StackPane">
|
||||
<fx:define>
|
||||
<Insets fx:id="insets" topRightBottomLeft="8" />
|
||||
</fx:define>
|
||||
<VBox fx:id="list" styleClass="jfx-list-view" maxHeight="150" maxWidth="400">
|
||||
<Label padding="${insets}" text="%install.modpack" />
|
||||
<JFXButton fx:id="btnLocal" prefWidth="${list.width}" onMouseClicked="#onChooseLocalFile">
|
||||
<graphic>
|
||||
<BorderPane mouseTransparent="true">
|
||||
<left>
|
||||
<TwoLineListItem title="%modpack.choose.local" subtitle="%modpack.choose.local.detail" />
|
||||
</left>
|
||||
<right>
|
||||
<fx:include BorderPane.alignment="CENTER" source="/assets/svg/arrow-right.fxml"/>
|
||||
</right>
|
||||
</BorderPane>
|
||||
</graphic>
|
||||
</JFXButton>
|
||||
<JFXButton fx:id="btnRemote" prefWidth="${list.width}" onMouseClicked="#onChooseRemoteFile">
|
||||
<graphic>
|
||||
<BorderPane mouseTransparent="true">
|
||||
<left>
|
||||
<TwoLineListItem title="%modpack.choose.remote" subtitle="%modpack.choose.remote.detail" />
|
||||
</left>
|
||||
<right>
|
||||
<fx:include BorderPane.alignment="CENTER" source="/assets/svg/arrow-right.fxml"/>
|
||||
</right>
|
||||
</BorderPane>
|
||||
</graphic>
|
||||
</JFXButton>
|
||||
</VBox>
|
||||
</fx:root>
|
||||
@@ -24,7 +24,7 @@
|
||||
<BorderPane><left><Label text="%archive.version"/></left><right><Label fx:id="lblVersion" /></right></BorderPane>
|
||||
<BorderPane><left><Label text="%archive.author"/></left><right><Label fx:id="lblAuthor" /></right></BorderPane>
|
||||
<BorderPane>
|
||||
<left><JFXButton fx:id="btnDescription" onMouseClicked="#onDescribe" text="%modpack.wizard.step.3" styleClass="jfx-button-border" /></left>
|
||||
<left><JFXButton fx:id="btnDescription" onMouseClicked="#onDescribe" text="%modpack.description" styleClass="jfx-button-border" /></left>
|
||||
<right><JFXButton buttonType="RAISED" fx:id="btnInstall" onMouseClicked="#onInstall" text="%button.install" styleClass="jfx-button-raised" /></right>
|
||||
</BorderPane>
|
||||
</ComponentList>
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import com.jfoenix.controls.JFXButton?>
|
||||
<?import com.jfoenix.controls.JFXCheckBox?>
|
||||
<?import com.jfoenix.controls.JFXComboBox?>
|
||||
<?import javafx.collections.FXCollections?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.web.WebView?>
|
||||
<?import java.lang.String?>
|
||||
<fx:root xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
type="StackPane"
|
||||
style="-fx-background-color: white; -fx-padding: 3 0 3 0;">
|
||||
<?import javafx.collections.FXCollections?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.ToggleButton?>
|
||||
<?import javafx.scene.control.ToggleGroup?>
|
||||
<?import javafx.scene.layout.BorderPane?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.web.WebView?>
|
||||
|
||||
<fx:root style="-fx-background-color: white; -fx-padding: 3 0 3 0;" type="StackPane" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<VBox spacing="3">
|
||||
<BorderPane style="-fx-padding: 0 3 0 3;">
|
||||
<left>
|
||||
<HBox alignment="CENTER_LEFT" style="-fx-padding: 0 0 0 4;" spacing="3">
|
||||
<HBox alignment="CENTER_LEFT" spacing="3" style="-fx-padding: 0 0 0 4;">
|
||||
<Label text="%logwindow.show_lines" />
|
||||
<JFXComboBox fx:id="cboLines">
|
||||
<items>
|
||||
@@ -29,19 +33,19 @@
|
||||
</left>
|
||||
<right>
|
||||
<HBox spacing="3">
|
||||
<ToggleButton styleClass="log-toggle" style="-fx-background-color: #F7A699;" fx:id="btnFatals" text="0 fatals" selected="true">
|
||||
<ToggleButton fx:id="btnFatals" selected="true" style="-fx-background-color: #F7A699;" styleClass="log-toggle" text="0 fatals">
|
||||
<toggleGroup><ToggleGroup /></toggleGroup>
|
||||
</ToggleButton>
|
||||
<ToggleButton styleClass="log-toggle" style="-fx-background-color: #FFCCBB;" fx:id="btnErrors" text="0 errors" selected="true">
|
||||
<ToggleButton fx:id="btnErrors" selected="true" style="-fx-background-color: #FFCCBB;" styleClass="log-toggle" text="0 errors">
|
||||
<toggleGroup><ToggleGroup /></toggleGroup>
|
||||
</ToggleButton>
|
||||
<ToggleButton styleClass="log-toggle" style="-fx-background-color: #FFEECC;" fx:id="btnWarns" text="0 warns" selected="true">
|
||||
<ToggleButton fx:id="btnWarns" selected="true" style="-fx-background-color: #FFEECC;" styleClass="log-toggle" text="0 warns">
|
||||
<toggleGroup><ToggleGroup /></toggleGroup>
|
||||
</ToggleButton>
|
||||
<ToggleButton styleClass="log-toggle" style="-fx-background-color: #FBFBFB;" fx:id="btnInfos" text="0 infos" selected="true">
|
||||
<ToggleButton fx:id="btnInfos" selected="true" style="-fx-background-color: #FBFBFB;" styleClass="log-toggle" text="0 infos">
|
||||
<toggleGroup><ToggleGroup /></toggleGroup>
|
||||
</ToggleButton>
|
||||
<ToggleButton styleClass="log-toggle" style="-fx-background-color: #EEE9E0;" fx:id="btnDebugs" text="0 debugs" selected="true">
|
||||
<ToggleButton fx:id="btnDebugs" selected="true" style="-fx-background-color: #EEE9E0;" styleClass="log-toggle" text="0 debugs">
|
||||
<toggleGroup><ToggleGroup /></toggleGroup>
|
||||
</ToggleButton>
|
||||
</HBox>
|
||||
@@ -50,7 +54,8 @@
|
||||
<StackPane style="-fx-border: 1 0 1 0; -fx-border-color: #dddddd;" VBox.vgrow="ALWAYS">
|
||||
<WebView fx:id="webView" />
|
||||
</StackPane>
|
||||
<HBox alignment="CENTER_RIGHT" style="-fx-padding: 0 3 0 3;" spacing="3">
|
||||
<HBox alignment="CENTER_RIGHT" spacing="3" style="-fx-padding: 0 3 0 3;">
|
||||
<JFXCheckBox fx:id="autoscroll" selected="true" text="%logwindow.autoscroll" />
|
||||
<JFXButton onMouseClicked="#onTerminateGame" text="%logwindow.terminate_game" />
|
||||
<JFXButton onMouseClicked="#onClear" text="%button.clear" />
|
||||
</HBox>
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import com.jfoenix.controls.*?>
|
||||
<?import com.jfoenix.validation.RequiredFieldValidator?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import org.jackhuang.hmcl.ui.construct.ComponentList?>
|
||||
<fx:root xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
type="StackPane">
|
||||
<fx:define>
|
||||
<Insets fx:id="insets" top="5" bottom="12"/>
|
||||
<Insets fx:id="componetListMargin" top="16" bottom="16" />
|
||||
</fx:define>
|
||||
<ScrollPane fx:id="scroll" fitToWidth="true" fitToHeight="true">
|
||||
<BorderPane style="-fx-padding: 16;">
|
||||
<top>
|
||||
<Label wrapText="true" textAlignment="JUSTIFY" text="%modpack.wizard.step.initialization.warning"/>
|
||||
</top>
|
||||
<center>
|
||||
<ComponentList BorderPane.margin="$componetListMargin">
|
||||
<BorderPane>
|
||||
<left>
|
||||
<Label text="%modpack.wizard.step.initialization.exported_version" />
|
||||
</left>
|
||||
<right>
|
||||
<Label fx:id="lblVersionName"/>
|
||||
</right>
|
||||
</BorderPane>
|
||||
<JFXTextField labelFloat="true" promptText="%modpack.name" fx:id="txtModpackName"
|
||||
StackPane.margin="$insets">
|
||||
<validators>
|
||||
<RequiredFieldValidator message="%modpack.not_a_valid_name"/>
|
||||
</validators>
|
||||
</JFXTextField>
|
||||
<JFXTextField labelFloat="true" promptText="%archive.author" fx:id="txtModpackAuthor"
|
||||
StackPane.margin="$insets">
|
||||
<validators>
|
||||
<RequiredFieldValidator/>
|
||||
</validators>
|
||||
</JFXTextField>
|
||||
<JFXTextField labelFloat="true" promptText="%archive.version" fx:id="txtModpackVersion" text="1.0"
|
||||
StackPane.margin="$insets">
|
||||
<validators>
|
||||
<RequiredFieldValidator/>
|
||||
</validators>
|
||||
</JFXTextField>
|
||||
<JFXTextArea labelFloat="true" promptText="%modpack.desc" fx:id="txtModpackDescription"
|
||||
StackPane.margin="$insets" minHeight="400" />
|
||||
<BorderPane>
|
||||
<left>
|
||||
<Label text="%modpack.wizard.step.initialization.include_launcher"/>
|
||||
</left>
|
||||
<right>
|
||||
<JFXToggleButton fx:id="chkIncludeLauncher" size="7" maxHeight="10.0" minHeight="10.0"/>
|
||||
</right>
|
||||
</BorderPane>
|
||||
</ComponentList>
|
||||
</center>
|
||||
<bottom>
|
||||
<HBox alignment="CENTER_RIGHT">
|
||||
<JFXButton fx:id="btnNext" onMouseClicked="#onNext" prefWidth="100" prefHeight="40" buttonType="RAISED"
|
||||
text="%wizard.next" styleClass="jfx-button-raised"/>
|
||||
</HBox>
|
||||
</bottom>
|
||||
</BorderPane>
|
||||
</ScrollPane>
|
||||
</fx:root>
|
||||
@@ -37,5 +37,17 @@
|
||||
</BorderPane>
|
||||
</graphic>
|
||||
</JFXButton>
|
||||
<JFXButton fx:id="btnServer" prefWidth="${list.width}">
|
||||
<graphic>
|
||||
<BorderPane mouseTransparent="true">
|
||||
<left>
|
||||
<TwoLineListItem title="%modpack.type.server" subtitle="%modpack.type.server.export" />
|
||||
</left>
|
||||
<right>
|
||||
<fx:include BorderPane.alignment="CENTER" source="/assets/svg/arrow-right.fxml"/>
|
||||
</right>
|
||||
</BorderPane>
|
||||
</graphic>
|
||||
</JFXButton>
|
||||
</VBox>
|
||||
</fx:root>
|
||||
@@ -165,7 +165,7 @@ launch.advice.different_platform=Your OS is 64-Bit but your Java is 32-Bit. The
|
||||
launch.advice.forge2760_liteloader=Forge 2760 and higher are not compatible with LiteLoader, please consider upgrading Forge to 2773 or later.
|
||||
launch.advice.java8_1_13=Minecraft 1.13 and later can only run on Java 8 or later.
|
||||
launch.advice.java8_51_1_13=Minecraft 1.13 may crash on Java 8 earlier than 1.8.0_51. Please install the latest version of Java 8.
|
||||
launch.advice.java9=You cannot launch Minecraft 1.12 oer earlier with Java 9 or later versions of Java.
|
||||
launch.advice.java9=You cannot launch Minecraft 1.12 or earlier with Java 9 or later versions of Java.
|
||||
launch.advice.newer_java=Java 8 is recommended to make the game faster. For many Minecraft 1.12 of higher, and most mods, Java 8 is required.
|
||||
launch.advice.not_enough_space=You have allocated too much memory, because the physical memory size is %dMB, your game may crash. The launcher attempt to launch.
|
||||
launch.advice.too_large_memory_for_32bit=You have allocated too much memory, because of your 32-Bit Java Runtime Environment, your game may crash. The maximum memory capacity for 32 bit systems is is 1024MB. The launcher will attempt to launch.
|
||||
@@ -180,7 +180,7 @@ launch.state.dependencies=Dependencies
|
||||
launch.state.done=Done
|
||||
launch.state.logging_in=Logging In
|
||||
launch.state.modpack=Loading modpack
|
||||
launch.state.waiting_launching=Launcing modpack
|
||||
launch.state.waiting_launching=Launching modpack
|
||||
launch.wrong_javadir=Invalid Java directory, default Java path will be applied.
|
||||
|
||||
launcher=Launcher
|
||||
@@ -203,6 +203,7 @@ login.enter_password=Please enter your password.
|
||||
logwindow.show_lines=Show Lines
|
||||
logwindow.terminate_game=Terminate Game
|
||||
logwindow.title=Log
|
||||
logwindow.autoscroll=Autoscroll
|
||||
|
||||
main_page=Home
|
||||
|
||||
@@ -217,13 +218,21 @@ message.warning=Warning
|
||||
|
||||
modpack=Modpack
|
||||
modpack.choose=Choose a modpack zip.
|
||||
modpack.choose.local=Import local modpack file
|
||||
modpack.choose.local.detail=You can drag the modpack file into this page to install
|
||||
modpack.choose.remote=Download modpack from Internet
|
||||
modpack.choose.remote.detail=Requires a direct download link to the remote modpack file
|
||||
modpack.choose.remote.tooltip=A direct download link to the remote modpack file
|
||||
modpack.desc=Describe your modpack, including precautions and changelog. Markdown and online pictures are supported.
|
||||
modpack.description=Description
|
||||
modpack.enter_name=Enter a name for this modpack.
|
||||
modpack.export=Export Modpack
|
||||
modpack.export.as=Export Modpack As...
|
||||
modpack.file_api=Modpack Download Link Prefix
|
||||
modpack.files.blueprints=BuildCraft blueprints
|
||||
modpack.files.config=Mod configs
|
||||
modpack.files.dumps=NEI debug output
|
||||
modpack.files.hmclversion_cfg=Launcher configuration file
|
||||
modpack.files.liteconfig=Mod configurations
|
||||
modpack.files.mods=Mods
|
||||
modpack.files.mods.voxelmods=VoxelMods (including VoxelMap) options
|
||||
@@ -253,6 +262,8 @@ modpack.type.hmcl=Hello Minecraft! Launcher
|
||||
modpack.type.hmcl.export=Can be imported by Hello Minecraft! Launcher
|
||||
modpack.type.multimc=MultiMC
|
||||
modpack.type.multimc.export=Can be imported by Hello Minecraft! Launcher and MultiMC
|
||||
modpack.type.server=Server Auto-Update Modpack
|
||||
modpack.type.server.export=Allow server manager updating game client remotely
|
||||
modpack.unsupported=Unsupported modpack, only HMCL, MultiMC, and Curse modpacks are supported.
|
||||
modpack.update=Game Update
|
||||
modpack.wizard=Exporting modpack wizard
|
||||
@@ -266,6 +277,7 @@ modpack.wizard.step.initialization.exported_version=Exported game version
|
||||
modpack.wizard.step.initialization.include_launcher=Include the launcher
|
||||
modpack.wizard.step.initialization.save=Export to...
|
||||
modpack.wizard.step.initialization.warning=Before creating a modpack, you should ensure that the game can launch successfully,\nand that your Minecraft is a release version.\nDo NOT add mods which cannot be redistributed.
|
||||
modpack.wizard.step.initialization.server=Click here for more information about server auto-update modpack
|
||||
|
||||
mods=Mods
|
||||
mods.add=Install mods
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# Contributors: gitfamhub
|
||||
# Contributors: poopingpenis
|
||||
about.copyright=Copyright
|
||||
about.copyright.statement=Copyright (c) 2019 huangyuhui.
|
||||
about.author=Autor
|
||||
@@ -69,8 +69,8 @@ archive.game_version=Juego
|
||||
archive.name=Nombre
|
||||
archive.version=Versión
|
||||
|
||||
assets.download=Descargar assets
|
||||
assets.download_all=Verificar integridad de assets
|
||||
assets.download=Descargando assets
|
||||
assets.download_all=Verificando integridad de assets
|
||||
|
||||
button.cancel=Cancelar
|
||||
button.clear=Despejar
|
||||
@@ -132,7 +132,7 @@ install.failed.downloading=Falló la instalación por algunos archivos no podien
|
||||
install.failed.downloading.detail=Falló bajar archivo: %s
|
||||
install.failed.downloading.timeout=Tiempo de espera agotado: %s
|
||||
install.failed.install_online=No se pudo reconocer el archivo instalador proveído
|
||||
install.failed.optifine_conflict=OptiFine y Forge ambos están instalados simultáneamente en Minecraft 1.13
|
||||
install.failed.optifine_conflict=Fabric, OptiFine y Forge están instalados simultáneamente en Minecraft 1.13
|
||||
install.failed.version_mismatch=El library requiere versión del juego %s, pero versión actual es %s.
|
||||
install.installer.choose=Escoja una versión de %s
|
||||
install.installer.fabric=Fabric
|
||||
@@ -143,7 +143,7 @@ install.installer.install_offline=Instalar/actualizar con archivo local
|
||||
install.installer.install_offline.extension=Instalador de Forge/OptiFine
|
||||
install.installer.install_offline.tooltip=Soporta importación de archivo instalador jar de Forge/OptiFine
|
||||
install.installer.install_online=Instalar en línea
|
||||
install.installer.install_online.tooltip=Soporta instalación de Forge, OptiFine, LiteLoader
|
||||
install.installer.install_online.tooltip=Soporta instalación de Fabric, Forge, OptiFine, LiteLoader
|
||||
install.installer.liteloader=LiteLoader
|
||||
install.installer.not_installed=%s no está instalado
|
||||
install.installer.optifine=OptiFine
|
||||
@@ -152,6 +152,7 @@ install.modpack=Instalar un modpack
|
||||
install.new_game=Instalar un juego nuevo
|
||||
install.new_game.already_exists=Esta versión ya existe.
|
||||
install.new_game.current_game_version=Versión de juego actual
|
||||
install.new_game.malformed=Nombre inválido
|
||||
install.select=Seleccione una operación
|
||||
install.success=Instalado exitosamente
|
||||
|
||||
@@ -217,8 +218,10 @@ message.warning=Advertencia
|
||||
modpack=Modpack
|
||||
modpack.choose=Escoja el modpack en archivo zip que quieras instalar.
|
||||
modpack.desc=Describa su modpack, incluyendo precauciones, changelog, soportando Markdown (también soportando imágenes en línea).
|
||||
modpack.description=Descripción
|
||||
modpack.enter_name=Entre su nombre deseado para este juego.
|
||||
modpack.export=Exportar modpack
|
||||
modpack.export.as=Exportar modpack como...
|
||||
modpack.files.blueprints=Planos de BuildCraft
|
||||
modpack.files.config=Mod configs
|
||||
modpack.files.dumps=NEI debug output
|
||||
@@ -244,11 +247,13 @@ modpack.task.install.error=Este archivo modpack no pudo ser reconocido. Sólo mo
|
||||
modpack.task.install.will=Instalar el modpack:
|
||||
modpack.type.curse=Curse
|
||||
modpack.type.curse.completion=Instalar archivos a modpack Curse
|
||||
modpack.type.curse.tolerable_error=Pero no podemos completar la descarga de todos los archivos de este modpack Curse. Puedes reintentar la descarga cargando la versión de juego correspondiente. Puedes reintentar varias veces en caso de problemas de red.
|
||||
modpack.type.curse.tolerable_error=No podemos completar la descarga de todos los archivos de este modpack Curse. Puedes reintentar la descarga cargando la versión de juego correspondiente. Puedes reintentar varias veces en caso de problemas de red.
|
||||
modpack.type.curse.error=No se puede completar este modpack Curse. Por favor reintente.
|
||||
modpack.type.curse.not_found=Algunos recursos requeridos fueron borrados y no se pueden descargar. Considere usando la última versíon u otros modpacks.
|
||||
modpack.type.hmcl=HMCL
|
||||
modpack.type.hmcl.export=Puede ser importado por Hello Minecraft! Launcher
|
||||
modpack.type.multimc=MultiMC
|
||||
modpack.type.multimc.export=Puede ser importado por HMCL y MultiMC
|
||||
modpack.unsupported=Modpack no soportado, sólo modpacks HMCL, MultiMC, Curse son soportados.
|
||||
modpack.update=Actualizando juego
|
||||
modpack.wizard=Exportando asistente de modpack
|
||||
@@ -256,7 +261,8 @@ modpack.wizard.step.1=Opciones básicas
|
||||
modpack.wizard.step.1.title=Establecer las opciones básicas del modpack.
|
||||
modpack.wizard.step.2=Selección de archivos
|
||||
modpack.wizard.step.2.title=Escoge los archivos que quieres tener en el modpack.
|
||||
modpack.wizard.step.3=Descripción
|
||||
modpack.wizard.step.3=Tipo de modpack
|
||||
modpack.wizard.step.3.title=Escoger el formato del modpack.
|
||||
modpack.wizard.step.initialization.exported_version=La versión del juego exportado
|
||||
modpack.wizard.step.initialization.include_launcher=Incluir el launcher
|
||||
modpack.wizard.step.initialization.save=Escoger ruta de exportación de los archivos del juego
|
||||
@@ -271,7 +277,7 @@ mods.enable=Habilitar
|
||||
mods.disable=Deshabilitar
|
||||
mods.name=Nombre
|
||||
mods.remove=Borrar
|
||||
mods.not_modded=Debes instalar un modloader primero (Fabric, Forge or LiteLoader)
|
||||
mods.not_modded=Debes instalar un modloader primero (Fabric, Forge, o LiteLoader)
|
||||
|
||||
datapack=Data packs
|
||||
datapack.add=Añadir data pack
|
||||
@@ -303,7 +309,7 @@ world.time=EEE, MMM d, yyyy HH:mm:ss
|
||||
|
||||
profile=Directorios de juego
|
||||
profile.already_exists=Este nombre ya existe, por favor considere otro.
|
||||
profile.default=directorio actual
|
||||
profile.default=Directorio actual
|
||||
profile.home=Hogar de usuario
|
||||
profile.instance_directory=Directorio de juego
|
||||
profile.instance_directory.choose=Escoja directorio de juego
|
||||
@@ -318,7 +324,7 @@ selector.choose=Escoger
|
||||
selector.choose_file=Seleccionar archivo
|
||||
selector.custom=Personalizado
|
||||
|
||||
settings=Configuraciones de juego
|
||||
settings=Configs. de juego
|
||||
|
||||
settings.advanced=Configuraciones avanzadas
|
||||
settings.advanced.dont_check_game_completeness=No verificar cuán completo está el juego
|
||||
@@ -382,11 +388,11 @@ settings.tabs.installers=Instaladores
|
||||
settings.type=Tipo de configuricón de la versión
|
||||
settings.type.global=Configuración global de juego (todo compartido)
|
||||
settings.type.global.manage=Configuración global de juego
|
||||
settings.type.global.edit=Configurar configuración global de juego
|
||||
settings.type.special.enable=Habilitar configuraciones especializadas para este juego
|
||||
settings.type.global.edit=Configurar config. global de juego
|
||||
settings.type.special.enable=Habilitar configs. especializadas para este juego
|
||||
|
||||
sponsor.bmclapi=Para obtener un servicio de descargas estable, considere apoyar a BMCLAPI. Clic aquí para más información
|
||||
sponsor.hmcl=Hello Minecraft! Launcher es un Minecraft launcher gratis y do código abierto que habalita sus usuarios a fácilmente gestionar multiples instalaciones de Minecraft aparte. Usamos Afdian para que podamos continuar pagando por nuestro hosting y desarrollo. Clic aquí para más información.
|
||||
sponsor.hmcl=Hello Minecraft! Launcher es un Minecraft launcher gratis y de código abierto que habilita sus usuarios a fácilmente gestionar multiples instalaciones de Minecraft aparte. Usamos Afdian para que podamos continuar pagando por nuestro alojamiento y desarrollo. Clic aquí para más información.
|
||||
|
||||
update=Actualizar
|
||||
update.accept=Actualizar
|
||||
@@ -438,4 +444,4 @@ version.update=Actualizar modpack
|
||||
wizard.prev=< Prev
|
||||
wizard.failed=Fallo
|
||||
wizard.finish=Acabar
|
||||
wizard.next=Próx >
|
||||
wizard.next=Próx >
|
||||
@@ -218,6 +218,7 @@ message.warning=Предупреждение
|
||||
modpack=Модпак
|
||||
modpack.choose=Выберите zip-файл модпака, который вы хотите установить.
|
||||
modpack.desc=Опишите свой модпак, включая меры предосторожности, журнал изменений, поддерживая Markdown (также поддерживая онлайн-изображения).
|
||||
modpack.description=описание
|
||||
modpack.enter_name=Введите желаемое имя для этой версии.
|
||||
modpack.export=Экспортировать модпак
|
||||
modpack.export.as=Экспорт модпака как...
|
||||
|
||||
@@ -215,13 +215,21 @@ message.warning=警告
|
||||
|
||||
modpack=整合包
|
||||
modpack.choose=選擇要安裝的遊戲整合包檔案
|
||||
modpack.choose.local=導入本地整合包檔案
|
||||
modpack.choose.local.detail=你可以直接將整合包檔案拖入本頁面以安裝
|
||||
modpack.choose.remote=從網路下載整合包
|
||||
modpack.choose.remote.detail=需要提供整合包的下載連結
|
||||
modpack.choose.remote.tooltip=要下載的整合包的連結
|
||||
modpack.desc=描述你要製作的整合包,比如整合包注意事項和更新記錄,支援 Markdown(圖片請上傳至網路)。
|
||||
modpack.description=整合包描述
|
||||
modpack.enter_name=給遊戲取個你喜歡的名字
|
||||
modpack.export=匯出整合包
|
||||
modpack.export.as=請選擇整合包類型。若你無法決定,請選擇 HMCL 類型。
|
||||
modpack.file_api=整合包下載連結前綴
|
||||
modpack.files.blueprints=BuildCraft 藍圖
|
||||
modpack.files.config=Mod 模組設定檔案
|
||||
modpack.files.dumps=NEI 調校輸出
|
||||
modpack.files.hmclversion_cfg=啟動器設定檔案
|
||||
modpack.files.liteconfig=Mod 模組設定檔案
|
||||
modpack.files.mods=Mod 模組
|
||||
modpack.files.mods.voxelmods=VoxelMods 設定,如小地圖
|
||||
@@ -251,6 +259,8 @@ modpack.type.hmcl=HMCL
|
||||
modpack.type.hmcl.export=可以被 Hello Minecraft! Launcher (HMCL) 導入
|
||||
modpack.type.multimc=MultiMC
|
||||
modpack.type.multimc.export=可以被 Hello Minecraft! Launcher (HMCL) 和 MultiMC 導入
|
||||
modpack.type.server=伺服器自動更新整合包
|
||||
modpack.type.server.export=允許伺服器管理員遠程更新遊戲用戶端
|
||||
modpack.unsupported=不支援該整合包。僅 HMCL、MultiMC、Curse 整合包受支援。
|
||||
modpack.update=正在升級整合包
|
||||
modpack.wizard=匯出整合包引導
|
||||
@@ -264,6 +274,7 @@ modpack.wizard.step.initialization.exported_version=要匯出的遊戲版本
|
||||
modpack.wizard.step.initialization.include_launcher=包含啟動器
|
||||
modpack.wizard.step.initialization.save=選擇要匯出到的遊戲整合包位置
|
||||
modpack.wizard.step.initialization.warning=在製作整合包前,請您確認您選擇的版本可以正常啟動,\n並保證您的 Minecraft 是正式版而非快照版,\n而且不應將不允許非官方途徑傳播的 Mod 模組、材質包等納入整合包。\n整合包會儲存您目前的下載來源設定
|
||||
modpack.wizard.step.initialization.server=點擊此處查看有關伺服器自動更新整合包的製作教學
|
||||
|
||||
mods=模組管理
|
||||
mods.add=新增模組
|
||||
|
||||
@@ -216,13 +216,21 @@ message.warning=警告
|
||||
|
||||
modpack=整合包
|
||||
modpack.choose=选择要安装的游戏整合包文件
|
||||
modpack.choose.local=导入本地整合包文件
|
||||
modpack.choose.local.detail=你可以直接将整合包文件拖入本页面以安装
|
||||
modpack.choose.remote=从互联网下载整合包
|
||||
modpack.choose.remote.detail=需要提供整合包的下载链接
|
||||
modpack.choose.remote.tooltip=要下载的整合包的链接
|
||||
modpack.desc=描述你要制作的整合包,比如整合包注意事项和更新记录,支持 Markdown(图片请用网络图)。
|
||||
modpack.description=整合包描述
|
||||
modpack.enter_name=给游戏起个你喜欢的名字
|
||||
modpack.export=导出整合包
|
||||
modpack.export.as=请选择整合包类型。若你无法决定,请选择 HMCL 类型。
|
||||
modpack.file_api=整合包下载链接前缀
|
||||
modpack.files.blueprints=BuildCraft 蓝图
|
||||
modpack.files.config=Mod 配置文件
|
||||
modpack.files.dumps=NEI 调试输出
|
||||
modpack.files.hmclversion_cfg=启动器配置文件
|
||||
modpack.files.liteconfig=Mod 配置文件
|
||||
modpack.files.mods=Mod
|
||||
modpack.files.mods.voxelmods=VoxelMods 配置,如小地图
|
||||
@@ -252,6 +260,8 @@ modpack.type.hmcl=HMCL
|
||||
modpack.type.hmcl.export=可以被 Hello Minecraft! Launcher (HMCL) 导入
|
||||
modpack.type.multimc=MultiMC
|
||||
modpack.type.multimc.export=可以被 Hello Minecraft! Launcher (HMCL) 和 MultiMC 导入
|
||||
modpack.type.server=服务器自动更新整合包
|
||||
modpack.type.server.export=允许服务器管理员远程更新游戏客户端
|
||||
modpack.unsupported=该整合包不被支持。仅 HMCL、MultiMC、Curse 整合包受支持。
|
||||
modpack.update=正在升级整合包
|
||||
modpack.wizard=导出整合包向导
|
||||
@@ -265,6 +275,7 @@ modpack.wizard.step.initialization.exported_version=要导出的游戏版本
|
||||
modpack.wizard.step.initialization.include_launcher=包含启动器
|
||||
modpack.wizard.step.initialization.save=选择要导出到的游戏整合包位置
|
||||
modpack.wizard.step.initialization.warning=在制作整合包前,请您确认您选择的版本可以正常启动,\n并保证您的 Minecraft 是正式版而非快照版,\n而且不应当将不允许非官方途径传播的 Mod、材质包等纳入整合包。\n整合包会保存您目前的下载源设置
|
||||
modpack.wizard.step.initialization.server=点击此处查看有关服务器自动更新整合包的制作教程
|
||||
|
||||
mods=模组管理
|
||||
mods.add=添加模组
|
||||
|
||||
+20
-11
@@ -17,10 +17,11 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.auth.authlibinjector;
|
||||
|
||||
import org.jackhuang.hmcl.auth.AuthenticationException;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilProvider;
|
||||
import org.jackhuang.hmcl.util.gson.UUIDTypeAdapter;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -33,32 +34,40 @@ public class AuthlibInjectorProvider implements YggdrasilProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getAuthenticationURL() {
|
||||
return NetworkUtils.toURL(apiRoot + "authserver/authenticate");
|
||||
public URL getAuthenticationURL() throws AuthenticationException {
|
||||
return toURL(apiRoot + "authserver/authenticate");
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getRefreshmentURL() {
|
||||
return NetworkUtils.toURL(apiRoot + "authserver/refresh");
|
||||
public URL getRefreshmentURL() throws AuthenticationException {
|
||||
return toURL(apiRoot + "authserver/refresh");
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getValidationURL() {
|
||||
return NetworkUtils.toURL(apiRoot + "authserver/validate");
|
||||
public URL getValidationURL() throws AuthenticationException {
|
||||
return toURL(apiRoot + "authserver/validate");
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getInvalidationURL() {
|
||||
return NetworkUtils.toURL(apiRoot + "authserver/invalidate");
|
||||
public URL getInvalidationURL() throws AuthenticationException {
|
||||
return toURL(apiRoot + "authserver/invalidate");
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getProfilePropertiesURL(UUID uuid) {
|
||||
return NetworkUtils.toURL(apiRoot + "sessionserver/session/minecraft/profile/" + UUIDTypeAdapter.fromUUID(uuid));
|
||||
public URL getProfilePropertiesURL(UUID uuid) throws AuthenticationException {
|
||||
return toURL(apiRoot + "sessionserver/session/minecraft/profile/" + UUIDTypeAdapter.fromUUID(uuid));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return apiRoot;
|
||||
}
|
||||
|
||||
private URL toURL(String url) throws AuthenticationException {
|
||||
try {
|
||||
return new URL(url);
|
||||
} catch (MalformedURLException e) {
|
||||
throw new AuthenticationException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,8 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.auth.yggdrasil;
|
||||
|
||||
import org.jackhuang.hmcl.auth.AuthenticationException;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -25,14 +27,14 @@ import java.util.UUID;
|
||||
*/
|
||||
public interface YggdrasilProvider {
|
||||
|
||||
URL getAuthenticationURL();
|
||||
URL getAuthenticationURL() throws AuthenticationException;
|
||||
|
||||
URL getRefreshmentURL();
|
||||
URL getRefreshmentURL() throws AuthenticationException;
|
||||
|
||||
URL getValidationURL();
|
||||
URL getValidationURL() throws AuthenticationException;
|
||||
|
||||
URL getInvalidationURL();
|
||||
URL getInvalidationURL() throws AuthenticationException;
|
||||
|
||||
URL getProfilePropertiesURL(UUID uuid);
|
||||
URL getProfilePropertiesURL(UUID uuid) throws AuthenticationException;
|
||||
|
||||
}
|
||||
@@ -287,7 +287,8 @@ public class DefaultLauncher extends Launcher {
|
||||
Process process;
|
||||
try {
|
||||
ProcessBuilder builder = new ProcessBuilder(rawCommandLine).directory(runDirectory);
|
||||
builder.environment().put("APPDATA", options.getGameDir().getAbsoluteFile().getParent());
|
||||
String appdata = options.getGameDir().getAbsoluteFile().getParent();
|
||||
if (appdata != null) builder.environment().put("APPDATA", appdata);
|
||||
process = builder.start();
|
||||
} catch (IOException e) {
|
||||
throw new ProcessCreationException(e);
|
||||
|
||||
@@ -62,10 +62,10 @@ public final class MinecraftInstanceTask<T> extends Task<Void> {
|
||||
Path root = fs.getPath(subDirectory);
|
||||
|
||||
if (Files.exists(root))
|
||||
Files.walkFileTree(fs.getPath(subDirectory), new SimpleFileVisitor<Path>() {
|
||||
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
String relativePath = root.relativize(file).normalize().toString();
|
||||
String relativePath = root.relativize(file).normalize().toString().replace(File.separatorChar, '/');
|
||||
overrides.add(new ModpackConfiguration.FileInformation(relativePath, encodeHex(digest("SHA-1", file))));
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ public interface ModAdviser {
|
||||
List<String> MODPACK_BLACK_LIST = Lang.immutableListOf(
|
||||
"usernamecache.json", "usercache.json", // Minecraft
|
||||
"launcher_profiles.json", "launcher.pack.lzma", // Minecraft Launcher
|
||||
"pack.json", "launcher.jar", "hmclmc.log", "cache", // HMCL
|
||||
"pack.json", "launcher.jar", "hmclmc.log", "cache", "modpack.cfg", // HMCL
|
||||
"manifest.json", "minecraftinstance.json", ".curseclient", // Curse
|
||||
"minetweaker.log", // Mods
|
||||
".fabric", ".mixin.out", // Fabric
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package org.jackhuang.hmcl.mod;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -85,4 +86,16 @@ public final class Modpack {
|
||||
public Modpack setManifest(Object manifest) {
|
||||
return new Modpack(name, author, version, gameVersion, description, encoding, manifest);
|
||||
}
|
||||
|
||||
public static boolean acceptFile(String path, List<String> blackList, List<String> whiteList) {
|
||||
if (path.isEmpty())
|
||||
return true;
|
||||
for (String s : blackList)
|
||||
if (path.equals(s))
|
||||
return false;
|
||||
for (String s : whiteList)
|
||||
if (path.equals(s))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,15 @@ public class ModpackInstallTask<T> extends Task<Void> {
|
||||
private final List<ModpackConfiguration.FileInformation> overrides;
|
||||
private final Predicate<String> callback;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param modpackFile a zip file
|
||||
* @param dest destination to store unpacked files
|
||||
* @param charset charset of the zip file
|
||||
* @param subDirectory the subdirectory of zip file to unpack
|
||||
* @param callback test whether the file (given full path) in zip file should be unpacked or not
|
||||
* @param oldConfiguration old modpack information if upgrade
|
||||
*/
|
||||
public ModpackInstallTask(File modpackFile, File dest, Charset charset, String subDirectory, Predicate<String> callback, ModpackConfiguration<T> oldConfiguration) {
|
||||
this.modpackFile = modpackFile;
|
||||
this.dest = dest;
|
||||
|
||||
@@ -52,7 +52,6 @@ public final class CurseCompletionTask extends Task<Void> {
|
||||
private final ModManager modManager;
|
||||
private final String version;
|
||||
private CurseManifest manifest;
|
||||
private final List<Task<?>> dependents = new LinkedList<>();
|
||||
private final List<Task<?>> dependencies = new LinkedList<>();
|
||||
|
||||
/**
|
||||
@@ -94,11 +93,6 @@ public final class CurseCompletionTask extends Task<Void> {
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Task<?>> getDependents() {
|
||||
return dependents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
if (manifest == null)
|
||||
|
||||
@@ -113,7 +113,7 @@ public final class CurseManifest {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param f the CurseForge modpack file.
|
||||
* @param zip the CurseForge modpack file.
|
||||
* @throws IOException if the file is not a valid zip file.
|
||||
* @throws JsonParseException if the manifest.json is missing or malformed.
|
||||
* @return the manifest.
|
||||
|
||||
+2
-11
@@ -21,6 +21,7 @@ import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
||||
import org.jackhuang.hmcl.game.GameVersion;
|
||||
import org.jackhuang.hmcl.mod.ModAdviser;
|
||||
import org.jackhuang.hmcl.mod.Modpack;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
@@ -67,17 +68,7 @@ public class MultiMCModpackExportTask extends Task<Void> {
|
||||
blackList.add(versionId + ".json");
|
||||
Logging.LOG.info("Compressing game files without some files in blacklist, including files or directories: usernamecache.json, asm, logs, backups, versions, assets, usercache.json, libraries, crash-reports, launcher_profiles.json, NVIDIA, TCNodeTracker");
|
||||
try (Zipper zip = new Zipper(output.toPath())) {
|
||||
zip.putDirectory(repository.getRunDirectory(versionId).toPath(), ".minecraft", path -> {
|
||||
if (path.isEmpty())
|
||||
return true;
|
||||
for (String s : blackList)
|
||||
if (path.equals(s))
|
||||
return false;
|
||||
for (String s : whitelist)
|
||||
if (path.equals(s))
|
||||
return true;
|
||||
return false;
|
||||
});
|
||||
zip.putDirectory(repository.getRunDirectory(versionId).toPath(), ".minecraft", path -> Modpack.acceptFile(path, blackList, whitelist));
|
||||
|
||||
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(versionId));
|
||||
String gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(versionId))
|
||||
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2019 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.mod.server;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
||||
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.task.GetTask;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jackhuang.hmcl.util.DigestUtils.digest;
|
||||
import static org.jackhuang.hmcl.util.Hex.encodeHex;
|
||||
|
||||
public class ServerModpackCompletionTask extends Task<Void> {
|
||||
|
||||
private final DefaultGameRepository repository;
|
||||
private final String version;
|
||||
private ModpackConfiguration<ServerModpackManifest> manifest;
|
||||
private GetTask dependent;
|
||||
private ServerModpackManifest remoteManifest;
|
||||
private final List<Task<?>> dependencies = new LinkedList<>();
|
||||
|
||||
public ServerModpackCompletionTask(DefaultDependencyManager dependencyManager, String version) {
|
||||
this.repository = dependencyManager.getGameRepository();
|
||||
this.version = version;
|
||||
|
||||
try {
|
||||
File manifestFile = repository.getModpackConfiguration(version);
|
||||
if (manifestFile.exists())
|
||||
this.manifest = JsonUtils.GSON.fromJson(FileUtils.readText(manifestFile), new TypeToken<ModpackConfiguration<ServerModpackManifest>>() {
|
||||
}.getType());
|
||||
} catch (Exception e) {
|
||||
Logging.LOG.log(Level.WARNING, "Unable to read CurseForge modpack manifest.json", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doPreExecute() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preExecute() throws Exception {
|
||||
if (manifest == null || StringUtils.isBlank(manifest.getManifest().getFileApi())) return;
|
||||
dependent = new GetTask(new URL(manifest.getManifest().getFileApi() + "/server-manifest.json"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Task<?>> getDependencies() {
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Task<?>> getDependents() {
|
||||
return dependent == null ? Collections.emptySet() : Collections.singleton(dependent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
if (manifest == null || StringUtils.isBlank(manifest.getManifest().getFileApi())) return;
|
||||
|
||||
try {
|
||||
remoteManifest = JsonUtils.fromNonNullJson(dependent.getResult(), ServerModpackManifest.class);
|
||||
} catch (JsonParseException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
||||
Path rootPath = repository.getVersionRoot(version).toPath();
|
||||
Map<String, ModpackConfiguration.FileInformation> files = manifest.getManifest().getFiles().stream()
|
||||
.collect(Collectors.toMap(ModpackConfiguration.FileInformation::getPath,
|
||||
Function.identity()));
|
||||
|
||||
Set<String> remoteFiles = remoteManifest.getFiles().stream().map(ModpackConfiguration.FileInformation::getPath)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// for files in new modpack
|
||||
for (ModpackConfiguration.FileInformation file : remoteManifest.getFiles()) {
|
||||
Path actualPath = rootPath.resolve(file.getPath());
|
||||
boolean download;
|
||||
if (!files.containsKey(file.getPath())) {
|
||||
// If old modpack does not have this entry, download it
|
||||
download = true;
|
||||
} else if (!Files.exists(actualPath)) {
|
||||
// If both old and new modpacks have this entry, but the file is missing...
|
||||
// Re-download it since network problem may cause file missing
|
||||
download = true;
|
||||
} else {
|
||||
// If user modified this entry file, we will not replace this file since this modified file is that user expects.
|
||||
String fileHash = encodeHex(digest("SHA-1", Files.newInputStream(actualPath)));
|
||||
String oldHash = files.get(file.getPath()).getHash();
|
||||
download = !Objects.equals(oldHash, file.getHash()) && Objects.equals(oldHash, fileHash);
|
||||
}
|
||||
|
||||
if (download) {
|
||||
dependencies.add(new FileDownloadTask(
|
||||
new URL(remoteManifest.getFileApi() + "/overrides/" + file.getPath()),
|
||||
actualPath.toFile(),
|
||||
new FileDownloadTask.IntegrityCheck("SHA-1", file.getHash())));
|
||||
}
|
||||
}
|
||||
|
||||
// If old modpack have this entry, and new modpack deleted it. Delete this file.
|
||||
for (ModpackConfiguration.FileInformation file : manifest.getManifest().getFiles()) {
|
||||
Path actualPath = rootPath.resolve(file.getPath());
|
||||
if (Files.exists(actualPath) && !remoteFiles.contains(file.getPath()))
|
||||
Files.deleteIfExists(actualPath);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doPostExecute() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postExecute() throws Exception {
|
||||
if (manifest == null || StringUtils.isBlank(manifest.getManifest().getFileApi())) return;
|
||||
File manifestFile = repository.getModpackConfiguration(version);
|
||||
FileUtils.writeText(manifestFile, JsonUtils.GSON.toJson(new ModpackConfiguration<>(remoteManifest, this.manifest.getType(), remoteManifest.getFiles())));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2019 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.mod.server;
|
||||
|
||||
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
||||
import org.jackhuang.hmcl.game.GameVersion;
|
||||
import org.jackhuang.hmcl.mod.ModAdviser;
|
||||
import org.jackhuang.hmcl.mod.Modpack;
|
||||
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.io.Zipper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*;
|
||||
import static org.jackhuang.hmcl.util.DigestUtils.digest;
|
||||
import static org.jackhuang.hmcl.util.Hex.encodeHex;
|
||||
|
||||
public class ServerModpackExportTask extends Task<Void> {
|
||||
private final DefaultGameRepository repository;
|
||||
private final String versionId;
|
||||
private final List<String> whitelist;
|
||||
private final File output;
|
||||
private final String modpackName;
|
||||
private final String modpackAuthor;
|
||||
private final String modpackVersion;
|
||||
private final String modpackDescription;
|
||||
private final String modpackFileApi;
|
||||
|
||||
public ServerModpackExportTask(DefaultGameRepository repository, String versionId, List<String> whitelist, String modpackName, String modpackAuthor, String modpackVersion, String modpackDescription, String modpackFileApi, File output) {
|
||||
this.repository = repository;
|
||||
this.versionId = versionId;
|
||||
this.whitelist = whitelist;
|
||||
this.output = output;
|
||||
this.modpackName = modpackName;
|
||||
this.modpackAuthor = modpackAuthor;
|
||||
this.modpackVersion = modpackVersion;
|
||||
this.modpackDescription = modpackDescription;
|
||||
this.modpackFileApi = modpackFileApi;
|
||||
|
||||
onDone().register(event -> {
|
||||
if (event.isFailed()) output.delete();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
ArrayList<String> blackList = new ArrayList<>(ModAdviser.MODPACK_BLACK_LIST);
|
||||
blackList.add(versionId + ".jar");
|
||||
blackList.add(versionId + ".json");
|
||||
Logging.LOG.info("Compressing game files without some files in blacklist, including files or directories: usernamecache.json, asm, logs, backups, versions, assets, usercache.json, libraries, crash-reports, launcher_profiles.json, NVIDIA, TCNodeTracker");
|
||||
try (Zipper zip = new Zipper(output.toPath())) {
|
||||
Path runDirectory = repository.getRunDirectory(versionId).toPath();
|
||||
List<ModpackConfiguration.FileInformation> files = new ArrayList<>();
|
||||
zip.putDirectory(runDirectory, "overrides", path -> {
|
||||
if (Modpack.acceptFile(path, blackList, whitelist)) {
|
||||
Path file = runDirectory.resolve(path);
|
||||
if (Files.isRegularFile(file)) {
|
||||
String relativePath = runDirectory.relativize(file).normalize().toString().replace(File.separatorChar, '/');
|
||||
files.add(new ModpackConfiguration.FileInformation(relativePath, encodeHex(digest("SHA-1", file))));
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(versionId));
|
||||
String gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(versionId))
|
||||
.orElseThrow(() -> new IOException("Cannot parse the version of " + versionId));
|
||||
List<ServerModpackManifest.Addon> addons = new ArrayList<>();
|
||||
addons.add(new ServerModpackManifest.Addon(MINECRAFT.getPatchId(), gameVersion));
|
||||
analyzer.getVersion(FORGE).ifPresent(forgeVersion ->
|
||||
addons.add(new ServerModpackManifest.Addon(FORGE.getPatchId(), forgeVersion)));
|
||||
analyzer.getVersion(LITELOADER).ifPresent(liteLoaderVersion ->
|
||||
addons.add(new ServerModpackManifest.Addon(LITELOADER.getPatchId(), liteLoaderVersion)));
|
||||
analyzer.getVersion(OPTIFINE).ifPresent(optifineVersion ->
|
||||
addons.add(new ServerModpackManifest.Addon(OPTIFINE.getPatchId(), optifineVersion)));
|
||||
analyzer.getVersion(FABRIC).ifPresent(fabricVersion ->
|
||||
addons.add(new ServerModpackManifest.Addon(FABRIC.getPatchId(), fabricVersion)));
|
||||
ServerModpackManifest manifest = new ServerModpackManifest(modpackName, modpackAuthor, modpackVersion, modpackDescription, StringUtils.removeSuffix(modpackFileApi, "/"), files, addons);
|
||||
zip.putTextFile(JsonUtils.GSON.toJson(manifest), "server-manifest.json");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2019 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.mod.server;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.download.GameBuilder;
|
||||
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
||||
import org.jackhuang.hmcl.mod.MinecraftInstanceTask;
|
||||
import org.jackhuang.hmcl.mod.Modpack;
|
||||
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
||||
import org.jackhuang.hmcl.mod.ModpackInstallTask;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class ServerModpackInstallTask extends Task<Void> {
|
||||
|
||||
private final File zipFile;
|
||||
private final Modpack modpack;
|
||||
private final ServerModpackManifest manifest;
|
||||
private final String name;
|
||||
private final DefaultGameRepository repository;
|
||||
private final List<Task<?>> dependencies = new LinkedList<>();
|
||||
private final List<Task<?>> dependents = new LinkedList<>();
|
||||
|
||||
public ServerModpackInstallTask(DefaultDependencyManager dependencyManager, File zipFile, Modpack modpack, ServerModpackManifest manifest, String name) {
|
||||
this.zipFile = zipFile;
|
||||
this.modpack = modpack;
|
||||
this.manifest = manifest;
|
||||
this.name = name;
|
||||
this.repository = dependencyManager.getGameRepository();
|
||||
File run = repository.getRunDirectory(name);
|
||||
|
||||
File json = repository.getModpackConfiguration(name);
|
||||
if (repository.hasVersion(name) && !json.exists())
|
||||
throw new IllegalArgumentException("Version " + name + " already exists.");
|
||||
|
||||
GameBuilder builder = dependencyManager.gameBuilder().name(name);
|
||||
for (ServerModpackManifest.Addon addon : manifest.getAddons()) {
|
||||
builder.version(addon.getId(), addon.getVersion());
|
||||
}
|
||||
|
||||
dependents.add(builder.buildAsync());
|
||||
onDone().register(event -> {
|
||||
if (event.isFailed())
|
||||
repository.removeVersionFromDisk(name);
|
||||
});
|
||||
|
||||
ModpackConfiguration<ServerModpackManifest> config = null;
|
||||
try {
|
||||
if (json.exists()) {
|
||||
config = JsonUtils.GSON.fromJson(FileUtils.readText(json), new TypeToken<ModpackConfiguration<ServerModpackManifest>>() {
|
||||
}.getType());
|
||||
|
||||
if (!MODPACK_TYPE.equals(config.getType()))
|
||||
throw new IllegalArgumentException("Version " + name + " is not a Curse modpack. Cannot update this version.");
|
||||
}
|
||||
} catch (JsonParseException | IOException ignore) {
|
||||
}
|
||||
dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), "/overrides", any -> true, config));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Task<?>> getDependents() {
|
||||
return dependents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Task<?>> getDependencies() {
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
dependencies.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), "/overrides", manifest, MODPACK_TYPE, repository.getModpackConfiguration(name)));
|
||||
}
|
||||
|
||||
public static final String MODPACK_TYPE = "Server";
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2019 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.mod.server;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.jackhuang.hmcl.mod.Modpack;
|
||||
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.gson.TolerableValidationException;
|
||||
import org.jackhuang.hmcl.util.gson.Validation;
|
||||
import org.jackhuang.hmcl.util.io.CompressingUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.MINECRAFT;
|
||||
|
||||
public class ServerModpackManifest implements Validation {
|
||||
private final String name;
|
||||
private final String author;
|
||||
private final String version;
|
||||
private final String description;
|
||||
private final String fileApi;
|
||||
private final List<ModpackConfiguration.FileInformation> files;
|
||||
private final List<Addon> addons;
|
||||
|
||||
public ServerModpackManifest() {
|
||||
this("", "", "", "", "", Collections.emptyList(), Collections.emptyList());
|
||||
}
|
||||
|
||||
public ServerModpackManifest(String name, String author, String version, String description, String fileApi, List<ModpackConfiguration.FileInformation> files, List<Addon> addons) {
|
||||
this.name = name;
|
||||
this.author = author;
|
||||
this.version = version;
|
||||
this.description = description;
|
||||
this.fileApi = fileApi;
|
||||
this.files = files;
|
||||
this.addons = addons;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getFileApi() {
|
||||
return fileApi;
|
||||
}
|
||||
|
||||
public List<ModpackConfiguration.FileInformation> getFiles() {
|
||||
return files;
|
||||
}
|
||||
|
||||
public List<Addon> getAddons() {
|
||||
return addons;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate() throws JsonParseException, TolerableValidationException {
|
||||
if (fileApi == null)
|
||||
throw new JsonParseException("ServerModpackManifest.fileApi cannot be blank");
|
||||
if (files == null)
|
||||
throw new JsonParseException("ServerModpackManifest.files cannot be null");
|
||||
}
|
||||
|
||||
public static final class Addon {
|
||||
private final String id;
|
||||
private final String version;
|
||||
|
||||
public Addon() {
|
||||
this("", "");
|
||||
}
|
||||
|
||||
public Addon(String id, String version) {
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param zip the CurseForge modpack file.
|
||||
* @throws IOException if the file is not a valid zip file.
|
||||
* @throws JsonParseException if the server-manifest.json is missing or malformed.
|
||||
* @return the manifest.
|
||||
*/
|
||||
public static Modpack readManifest(Path zip, Charset encoding) throws IOException, JsonParseException {
|
||||
String json = CompressingUtils.readTextZipEntry(zip, "server-manifest.json", encoding);
|
||||
ServerModpackManifest manifest = JsonUtils.fromNonNullJson(json, ServerModpackManifest.class);
|
||||
String gameVersion = manifest.getAddons().stream().filter(x -> MINECRAFT.getPatchId().equals(x.getId())).findAny().orElseThrow(() -> new IOException("Cannot find game version")).getVersion();
|
||||
return new Modpack(manifest.getName(), manifest.getAuthor(), manifest.getVersion(), gameVersion, manifest.getDescription(), encoding, manifest);
|
||||
}
|
||||
}
|
||||
@@ -323,7 +323,7 @@ public class FileDownloadTask extends Task<Void> {
|
||||
if (temp != null)
|
||||
temp.toFile().delete();
|
||||
exception = e;
|
||||
Logging.LOG.log(Level.WARNING, "Failed to download " + url + ", repeat times: " + repeat + 1, e);
|
||||
Logging.LOG.log(Level.WARNING, "Failed to download " + url + ", repeat times: " + (repeat + 1), e);
|
||||
} finally {
|
||||
closeFiles();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2019 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.util.function;
|
||||
|
||||
public interface ExceptionalPredicate<T, E extends Exception> {
|
||||
boolean test(T t) throws E;
|
||||
}
|
||||
@@ -18,6 +18,8 @@
|
||||
package org.jackhuang.hmcl.util.io;
|
||||
|
||||
|
||||
import org.jackhuang.hmcl.util.function.ExceptionalPredicate;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -25,7 +27,6 @@ import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Non thread-safe
|
||||
@@ -67,7 +68,7 @@ public final class Zipper implements Closeable {
|
||||
* @param targetDir the path of the directory in this zip file.
|
||||
* @param filter returns false if you do not want that file or directory
|
||||
*/
|
||||
public void putDirectory(Path source, String targetDir, Predicate<String> filter) throws IOException {
|
||||
public void putDirectory(Path source, String targetDir, ExceptionalPredicate<String, IOException> filter) throws IOException {
|
||||
Path root = fs.getPath(targetDir);
|
||||
Files.createDirectories(root);
|
||||
Files.walkFileTree(source, new SimpleFileVisitor<Path>() {
|
||||
|
||||
Reference in new issue
Block a user