Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa29f890de | ||
|
|
0f4e6a01bc | ||
|
|
1d3d8d6710 | ||
|
|
005e06c56f | ||
|
|
0a7c32b3cb | ||
|
|
82916a04ea | ||
|
|
ba2e419894 |
No files matched your search
@@ -34,6 +34,7 @@ import org.jackhuang.hmcl.util.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
@@ -101,6 +102,9 @@ public final class Launcher extends Application {
|
||||
LOG.info("Java Home: " + System.getProperty("java.home"));
|
||||
LOG.info("Current Directory: " + Paths.get("").toAbsolutePath());
|
||||
LOG.info("HMCL Directory: " + HMCL_DIRECTORY);
|
||||
LOG.info("Memory: " + Runtime.getRuntime().maxMemory() / 1024 / 1024 + "MB");
|
||||
ManagementFactory.getMemoryPoolMXBeans().stream().filter(bean -> bean.getName().equals("Metaspace")).findAny()
|
||||
.ifPresent(bean -> LOG.info("Metaspace: " + bean.getUsage().getUsed() / 1024 / 1024 + "MB"));
|
||||
|
||||
launch(args);
|
||||
} catch (Throwable e) { // Fucking JavaFX will suppress the exception and will break our crash reporter.
|
||||
|
||||
@@ -28,6 +28,7 @@ import javafx.scene.control.Tab;
|
||||
import javafx.scene.layout.StackPane;
|
||||
|
||||
import org.jackhuang.hmcl.download.game.GameAssetIndexDownloadTask;
|
||||
import org.jackhuang.hmcl.setting.EnumGameDirectory;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
import org.jackhuang.hmcl.ui.export.ExportWizardProvider;
|
||||
import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
|
||||
@@ -186,7 +187,8 @@ public final class VersionPage extends StackPane implements DecoratorPage {
|
||||
}
|
||||
|
||||
public static void deleteVersion(Profile profile, String version) {
|
||||
Controllers.confirmDialog(i18n("version.manage.remove.confirm", version), i18n("message.confirm"), () -> {
|
||||
boolean isIndependent = profile.getVersionSetting(version).getGameDirType() == EnumGameDirectory.VERSION_FOLDER;
|
||||
Controllers.confirmDialog(i18n(isIndependent ? "version.manage.remove.confirm.independent" : "version.manage.remove.confirm", version), i18n("message.confirm"), () -> {
|
||||
if (profile.getRepository().removeVersionFromDisk(version)) {
|
||||
profile.getRepository().refreshVersionsAsync().start();
|
||||
Controllers.navigate(null);
|
||||
|
||||
@@ -103,7 +103,7 @@ class AdditionalInstallersPage extends StackPane implements WizardPage {
|
||||
}
|
||||
|
||||
private String getVersion(String id) {
|
||||
return Optional.ofNullable(controller.getSettings().get(id)).map(it -> (RemoteVersion<?>) it).map(RemoteVersion::getSelfVersion).orElse(null);
|
||||
return Optional.ofNullable(controller.getSettings().get(id)).map(it -> (RemoteVersion) it).map(RemoteVersion::getSelfVersion).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -49,16 +49,16 @@ public final class DownloadWizardProvider implements WizardProvider {
|
||||
GameBuilder builder = profile.getDependency().gameBuilder();
|
||||
|
||||
builder.name((String) settings.get("name"));
|
||||
builder.gameVersion(((RemoteVersion<?>) settings.get("game")).getGameVersion());
|
||||
builder.gameVersion(((RemoteVersion) settings.get("game")).getGameVersion());
|
||||
|
||||
if (settings.containsKey("forge"))
|
||||
builder.version((RemoteVersion<?>) settings.get("forge"));
|
||||
builder.version((RemoteVersion) settings.get("forge"));
|
||||
|
||||
if (settings.containsKey("liteloader"))
|
||||
builder.version((RemoteVersion<?>) settings.get("liteloader"));
|
||||
builder.version((RemoteVersion) settings.get("liteloader"));
|
||||
|
||||
if (settings.containsKey("optifine"))
|
||||
builder.version((RemoteVersion<?>) settings.get("optifine"));
|
||||
builder.version((RemoteVersion) settings.get("optifine"));
|
||||
|
||||
return builder.buildAsync().finalized((a, b) -> profile.getRepository().refreshVersions());
|
||||
}
|
||||
|
||||
@@ -85,16 +85,16 @@ public final class InstallerWizardProvider implements WizardProvider {
|
||||
settings.put("success_message", i18n("install.success"));
|
||||
settings.put("failure_message", i18n("install.failed"));
|
||||
|
||||
Task ret = Task.empty();
|
||||
Task ret = Task.ofResult("version", () -> version);
|
||||
|
||||
if (settings.containsKey("forge"))
|
||||
ret = ret.then(profile.getDependency().installLibraryAsync(version, (RemoteVersion<?>) settings.get("forge")));
|
||||
ret = ret.then(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("forge")));
|
||||
|
||||
if (settings.containsKey("liteloader"))
|
||||
ret = ret.then(profile.getDependency().installLibraryAsync(version, (RemoteVersion<?>) settings.get("liteloader")));
|
||||
ret = ret.then(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("liteloader")));
|
||||
|
||||
if (settings.containsKey("optifine"))
|
||||
ret = ret.then(profile.getDependency().installLibraryAsync(version, (RemoteVersion<?>) settings.get("optifine")));
|
||||
ret = ret.then(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("optifine")));
|
||||
|
||||
return ret.then(profile.getRepository().refreshVersionsAsync());
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ public class InstallersPage extends StackPane implements WizardPage {
|
||||
|
||||
FXUtils.loadFXML(this, "/assets/fxml/download/installers.fxml");
|
||||
|
||||
String gameVersion = ((RemoteVersion<?>) controller.getSettings().get("game")).getGameVersion();
|
||||
String gameVersion = ((RemoteVersion) controller.getSettings().get("game")).getGameVersion();
|
||||
Validator hasVersion = new Validator(s -> !repository.hasVersion(s) && StringUtils.isNotBlank(s));
|
||||
hasVersion.setMessage(i18n("install.new_game.already_exists"));
|
||||
txtName.getValidators().add(hasVersion);
|
||||
@@ -103,7 +103,7 @@ public class InstallersPage extends StackPane implements WizardPage {
|
||||
}
|
||||
|
||||
private String getVersion(String id) {
|
||||
return ((RemoteVersion<?>) controller.getSettings().get(id)).getSelfVersion();
|
||||
return ((RemoteVersion) controller.getSettings().get(id)).getSelfVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -27,9 +27,8 @@ import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.jackhuang.hmcl.download.DownloadProvider;
|
||||
import org.jackhuang.hmcl.download.RemoteVersion;
|
||||
import org.jackhuang.hmcl.download.VersionList;
|
||||
import org.jackhuang.hmcl.download.game.GameRemoteVersionTag;
|
||||
import org.jackhuang.hmcl.download.game.GameVersionList;
|
||||
import org.jackhuang.hmcl.task.TaskExecutor;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
||||
@@ -79,7 +78,7 @@ public final class VersionsPage extends StackPane implements WizardPage, Refresh
|
||||
|
||||
FXUtils.loadFXML(this, "/assets/fxml/download/versions.fxml");
|
||||
|
||||
if (versionList instanceof GameVersionList) {
|
||||
if (versionList.hasType()) {
|
||||
centrePane.getChildren().setAll(checkPane, list);
|
||||
} else
|
||||
centrePane.getChildren().setAll(list);
|
||||
@@ -99,21 +98,21 @@ public final class VersionsPage extends StackPane implements WizardPage, Refresh
|
||||
}
|
||||
|
||||
private List<VersionsPageItem> loadVersions() {
|
||||
boolean isGameVersionList = versionList instanceof GameVersionList;
|
||||
return versionList.getVersions(gameVersion).stream()
|
||||
.filter(it -> {
|
||||
if (isGameVersionList)
|
||||
switch (((GameRemoteVersionTag) it.getTag()).getType()) {
|
||||
case RELEASE:
|
||||
return chkRelease.isSelected();
|
||||
case SNAPSHOT:
|
||||
return chkSnapshot.isSelected();
|
||||
default:
|
||||
return chkOld.isSelected();
|
||||
}
|
||||
else return true;
|
||||
if (it.getVersionType() == null)
|
||||
return true;
|
||||
switch (it.getVersionType()) {
|
||||
case RELEASE:
|
||||
return chkRelease.isSelected();
|
||||
case SNAPSHOT:
|
||||
return chkSnapshot.isSelected();
|
||||
case OLD:
|
||||
return chkOld.isSelected();
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.sorted()
|
||||
.map(VersionsPageItem::new).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@@ -25,8 +25,9 @@ import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.StackPane;
|
||||
|
||||
import org.jackhuang.hmcl.download.RemoteVersion;
|
||||
import org.jackhuang.hmcl.download.game.GameRemoteVersionTag;
|
||||
import org.jackhuang.hmcl.download.liteloader.LiteLoaderRemoteVersionTag;
|
||||
import org.jackhuang.hmcl.download.forge.ForgeRemoteVersion;
|
||||
import org.jackhuang.hmcl.download.game.GameRemoteVersion;
|
||||
import org.jackhuang.hmcl.download.liteloader.LiteLoaderRemoteVersion;
|
||||
import org.jackhuang.hmcl.download.optifine.OptiFineRemoteVersion;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
@@ -37,7 +38,7 @@ import java.util.Objects;
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class VersionsPageItem extends StackPane {
|
||||
private final RemoteVersion<?> remoteVersion;
|
||||
private final RemoteVersion remoteVersion;
|
||||
@FXML
|
||||
private Label lblSelfVersion;
|
||||
@FXML
|
||||
@@ -49,14 +50,14 @@ public final class VersionsPageItem extends StackPane {
|
||||
@FXML
|
||||
private StackPane imageViewContainer;
|
||||
|
||||
public VersionsPageItem(RemoteVersion<?> remoteVersion) {
|
||||
public VersionsPageItem(RemoteVersion remoteVersion) {
|
||||
this.remoteVersion = Objects.requireNonNull(remoteVersion);
|
||||
|
||||
FXUtils.loadFXML(this, "/assets/fxml/download/versions-list-item.fxml");
|
||||
lblSelfVersion.setText(remoteVersion.getSelfVersion());
|
||||
|
||||
if (remoteVersion.getTag() instanceof GameRemoteVersionTag) {
|
||||
switch (((GameRemoteVersionTag) remoteVersion.getTag()).getType()) {
|
||||
if (remoteVersion instanceof GameRemoteVersion) {
|
||||
switch (remoteVersion.getVersionType()) {
|
||||
case RELEASE:
|
||||
lblGameVersion.setText(i18n("version.game.release"));
|
||||
imageView.setImage(new Image("/assets/img/icon.png", 32, 32, false, true));
|
||||
@@ -70,13 +71,13 @@ public final class VersionsPageItem extends StackPane {
|
||||
imageView.setImage(new Image("/assets/img/grass.png", 32, 32, false, true));
|
||||
break;
|
||||
}
|
||||
} else if (remoteVersion.getTag() instanceof LiteLoaderRemoteVersionTag) {
|
||||
} else if (remoteVersion instanceof LiteLoaderRemoteVersion) {
|
||||
imageView.setImage(new Image("/assets/img/chicken.png", 32, 32, false, true));
|
||||
lblGameVersion.setText(remoteVersion.getGameVersion());
|
||||
} else if (remoteVersion instanceof OptiFineRemoteVersion) {
|
||||
// optifine has no icon.
|
||||
lblGameVersion.setText(remoteVersion.getGameVersion());
|
||||
} else { // forge
|
||||
} else if (remoteVersion instanceof ForgeRemoteVersion) {
|
||||
imageView.setImage(new Image("/assets/img/forge.png", 32, 32, false, true));
|
||||
lblGameVersion.setText(remoteVersion.getGameVersion());
|
||||
}
|
||||
@@ -84,7 +85,7 @@ public final class VersionsPageItem extends StackPane {
|
||||
leftPane.getChildren().remove(imageViewContainer);
|
||||
}
|
||||
|
||||
public RemoteVersion<?> getRemoteVersion() {
|
||||
public RemoteVersion getRemoteVersion() {
|
||||
return remoteVersion;
|
||||
}
|
||||
}
|
||||
@@ -334,7 +334,8 @@ version.launch_script.save=Save the launch script
|
||||
version.launch_script.success=Finished script creation, %s.
|
||||
version.manage.redownload_assets_index=Redownload Assets Index
|
||||
version.manage.remove=Delete this game
|
||||
version.manage.remove.confirm=Sure to remove version %s?
|
||||
version.manage.remove.confirm=Sure to remove game %s?
|
||||
version.manage.remove.confirm.independent=Since this game is in independent mode, deleting this game will result in all saved worlds belonging to this game removed simultaneously. Sure to remove game %s?
|
||||
version.manage.remove_libraries=Delete library files
|
||||
version.manage.rename=Rename this game
|
||||
version.manage.rename.message=Please enter the new name
|
||||
|
||||
@@ -335,6 +335,7 @@ version.launch_script.success=啓動腳本已生成完畢:%s
|
||||
version.manage.redownload_assets_index=重新下載資源配置(assets_index.json)
|
||||
version.manage.remove=刪除該版本
|
||||
version.manage.remove.confirm=真的要刪除版本 %s 嗎?
|
||||
version.manage.remove.confirm.independent=由於該遊戲使用了版本隔離,所以刪除該版本將導致該遊戲的存檔等數據一同被刪除,真的要刪除版本 %s 嗎?
|
||||
version.manage.remove_libraries=刪除所有庫文件
|
||||
version.manage.rename=重命名該版本
|
||||
version.manage.rename.message=請輸入要改成的名字
|
||||
|
||||
@@ -335,6 +335,7 @@ version.launch_script.success=启动脚本已生成完毕:%s
|
||||
version.manage.redownload_assets_index=重新下载资源配置(assets_index.json)
|
||||
version.manage.remove=删除该版本
|
||||
version.manage.remove.confirm=真的要删除版本 %s 吗?
|
||||
version.manage.remove.confirm.independent=由于该游戏使用了版本隔离,所以删除该版本将导致该游戏的存档等数据一同被删除,真的要删除版本 %s 吗?
|
||||
version.manage.remove_libraries=删除所有库文件
|
||||
version.manage.rename=重命名该版本
|
||||
version.manage.rename.message=请输入要改成的名字
|
||||
|
||||
@@ -64,8 +64,8 @@ public class BMCLAPIDownloadProvider implements DownloadProvider {
|
||||
return baseURL
|
||||
.replace("https://launchermeta.mojang.com", "https://bmclapi2.bangbang93.com")
|
||||
.replace("https://launcher.mojang.com", "https://bmclapi2.bangbang93.com")
|
||||
.replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/libraries")
|
||||
.replaceFirst("https?://files\\.minecraftforge\\.net/maven", "https://bmclapi2.bangbang93.com/maven")
|
||||
.replace("https://libraries.minecraft.net", "http://bmclapi2.bangbang93.com/libraries")
|
||||
.replaceFirst("https?://files\\.minecraftforge\\.net/maven", "http://bmclapi2.bangbang93.com/maven")
|
||||
.replace("http://dl.liteloader.com/versions/versions.json", "https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/versions.json")
|
||||
.replace("http://dl.liteloader.com/versions", "https://bmclapi2.bangbang93.com/maven")
|
||||
.replace("https://authlib-injector.yushi.moe", "https://bmclapi2.bangbang93.com/mirrors/authlib-injector");
|
||||
|
||||
@@ -28,6 +28,8 @@ import org.jackhuang.hmcl.game.DefaultGameRepository;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.task.ParallelTask;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.AutoTypingMap;
|
||||
import org.jackhuang.hmcl.util.ExceptionalFunction;
|
||||
|
||||
/**
|
||||
* Note: This class has no state.
|
||||
@@ -77,7 +79,7 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task installLibraryAsync(Version version, RemoteVersion<?> libraryVersion) {
|
||||
public Task installLibraryAsync(Version version, RemoteVersion libraryVersion) {
|
||||
if (libraryVersion instanceof ForgeRemoteVersion)
|
||||
return new ForgeInstallTask(this, version, (ForgeRemoteVersion) libraryVersion)
|
||||
.then(variables -> new LibrariesUniqueTask(variables.get("version")))
|
||||
@@ -97,4 +99,8 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
|
||||
throw new IllegalArgumentException("Remote library " + libraryVersion + " is unrecognized.");
|
||||
}
|
||||
|
||||
|
||||
public ExceptionalFunction<AutoTypingMap<String>, Task, ?> installLibraryAsync(RemoteVersion libraryVersion) {
|
||||
return var -> installLibraryAsync(var.get("version"), libraryVersion);
|
||||
}
|
||||
}
|
||||
@@ -67,8 +67,8 @@ public class DefaultGameBuilder extends GameBuilder {
|
||||
if (toolVersions.containsKey("optifine"))
|
||||
result = result.then(libraryTaskHelper(gameVersion, "optifine"));
|
||||
|
||||
for (RemoteVersion<?> remoteVersion : remoteVersions)
|
||||
result = result.then(var -> dependencyManager.installLibraryAsync(var.get("version"), remoteVersion));
|
||||
for (RemoteVersion remoteVersion : remoteVersions)
|
||||
result = result.then(dependencyManager.installLibraryAsync(remoteVersion));
|
||||
|
||||
return result;
|
||||
}).finalized((variables, isDependentsSucceeded) -> {
|
||||
|
||||
@@ -67,7 +67,7 @@ public interface DependencyManager {
|
||||
* @param libraryVersion the remote version of being installed library.
|
||||
* @return the task to install the specific library.
|
||||
*/
|
||||
Task installLibraryAsync(Version version, RemoteVersion<?> libraryVersion);
|
||||
Task installLibraryAsync(Version version, RemoteVersion libraryVersion);
|
||||
|
||||
/**
|
||||
* Get registered version list.
|
||||
|
||||
@@ -31,7 +31,7 @@ public abstract class GameBuilder {
|
||||
protected String name = "";
|
||||
protected String gameVersion = "";
|
||||
protected final Map<String, String> toolVersions = new HashMap<>();
|
||||
protected final Set<RemoteVersion<?>> remoteVersions = new HashSet<>();
|
||||
protected final Set<RemoteVersion> remoteVersions = new HashSet<>();
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
@@ -64,7 +64,7 @@ public abstract class GameBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public GameBuilder version(RemoteVersion<?> remoteVersion) {
|
||||
public GameBuilder version(RemoteVersion remoteVersion) {
|
||||
remoteVersions.add(remoteVersion);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ package org.jackhuang.hmcl.download;
|
||||
import org.jackhuang.hmcl.download.forge.ForgeVersionList;
|
||||
import org.jackhuang.hmcl.download.game.GameVersionList;
|
||||
import org.jackhuang.hmcl.download.liteloader.LiteLoaderVersionList;
|
||||
import org.jackhuang.hmcl.download.optifine.OptiFineVersionList;
|
||||
import org.jackhuang.hmcl.download.optifine.OptiFineBMCLVersionList;
|
||||
|
||||
/**
|
||||
* @see <a href="http://wiki.vg">http://wiki,vg</a>
|
||||
@@ -48,7 +48,7 @@ public class MojangDownloadProvider implements DownloadProvider {
|
||||
case "liteloader":
|
||||
return LiteLoaderVersionList.INSTANCE;
|
||||
case "optifine":
|
||||
return OptiFineVersionList.INSTANCE;
|
||||
return OptiFineBMCLVersionList.INSTANCE;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unrecognized version list id: " + id);
|
||||
}
|
||||
|
||||
@@ -27,26 +27,36 @@ import java.util.Objects;
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public class RemoteVersion<T> implements Comparable<RemoteVersion<T>> {
|
||||
public class RemoteVersion implements Comparable<RemoteVersion> {
|
||||
|
||||
private final String gameVersion;
|
||||
private final String selfVersion;
|
||||
private final String url;
|
||||
private final T tag;
|
||||
private final Type type;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param gameVersion the Minecraft version that this remote version suits.
|
||||
* @param selfVersion the version string of the remote version.
|
||||
* @param url the installer or universal jar URL.
|
||||
* @param tag some necessary information for Installer Task.
|
||||
* @param url the installer or universal jar URL.
|
||||
*/
|
||||
public RemoteVersion(String gameVersion, String selfVersion, String url, T tag) {
|
||||
public RemoteVersion(String gameVersion, String selfVersion, String url) {
|
||||
this(gameVersion, selfVersion, url, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param gameVersion the Minecraft version that this remote version suits.
|
||||
* @param selfVersion the version string of the remote version.
|
||||
* @param url the installer or universal jar URL.
|
||||
*/
|
||||
public RemoteVersion(String gameVersion, String selfVersion, String url, Type type) {
|
||||
this.gameVersion = Objects.requireNonNull(gameVersion);
|
||||
this.selfVersion = Objects.requireNonNull(selfVersion);
|
||||
this.url = Objects.requireNonNull(url);
|
||||
this.tag = tag;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getGameVersion() {
|
||||
@@ -57,14 +67,14 @@ public class RemoteVersion<T> implements Comparable<RemoteVersion<T>> {
|
||||
return selfVersion;
|
||||
}
|
||||
|
||||
public T getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public Type getVersionType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof RemoteVersion && Objects.equals(selfVersion, ((RemoteVersion) obj).selfVersion);
|
||||
@@ -80,13 +90,18 @@ public class RemoteVersion<T> implements Comparable<RemoteVersion<T>> {
|
||||
return new ToStringBuilder(this)
|
||||
.append("selfVersion", selfVersion)
|
||||
.append("gameVersion", gameVersion)
|
||||
.append("tag", tag)
|
||||
.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(RemoteVersion<T> o) {
|
||||
public int compareTo(RemoteVersion o) {
|
||||
// newer versions are smaller than older versions
|
||||
return VersionNumber.asVersion(o.selfVersion).compareTo(VersionNumber.asVersion(selfVersion));
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
RELEASE,
|
||||
SNAPSHOT,
|
||||
OLD
|
||||
}
|
||||
}
|
||||
@@ -26,18 +26,18 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
/**
|
||||
* The remote version list.
|
||||
*
|
||||
* @param <T> The type of {@code RemoteVersion<T>}, the type of tags.
|
||||
* @param <T> The subclass of {@code RemoteVersion}, the type of RemoteVersion.
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public abstract class VersionList<T> {
|
||||
public abstract class VersionList<T extends RemoteVersion> {
|
||||
|
||||
/**
|
||||
* the remote version list.
|
||||
* key: game version.
|
||||
* values: corresponding remote versions.
|
||||
*/
|
||||
protected final SimpleMultimap<String, RemoteVersion<T>> versions = new SimpleMultimap<String, RemoteVersion<T>>(HashMap::new, TreeSet::new);
|
||||
protected final SimpleMultimap<String, T> versions = new SimpleMultimap<String, T>(HashMap::new, TreeSet::new);
|
||||
|
||||
/**
|
||||
* True if the version list has been loaded.
|
||||
@@ -46,6 +46,8 @@ public abstract class VersionList<T> {
|
||||
return !versions.isEmpty();
|
||||
}
|
||||
|
||||
public abstract boolean hasType();
|
||||
|
||||
protected final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
|
||||
/**
|
||||
@@ -68,10 +70,10 @@ public abstract class VersionList<T> {
|
||||
});
|
||||
}
|
||||
|
||||
private Collection<RemoteVersion<T>> getVersionsImpl(String gameVersion) {
|
||||
private Collection<T> getVersionsImpl(String gameVersion) {
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
Collection<RemoteVersion<T>> ans = versions.get(gameVersion);
|
||||
Collection<T> ans = versions.get(gameVersion);
|
||||
return ans.isEmpty() ? versions.values() : ans;
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
@@ -84,7 +86,7 @@ public abstract class VersionList<T> {
|
||||
* @param gameVersion the Minecraft version that remote versions belong to
|
||||
* @return the collection of specific remote versions
|
||||
*/
|
||||
public final Collection<RemoteVersion<T>> getVersions(String gameVersion) {
|
||||
public final Collection<T> getVersions(String gameVersion) {
|
||||
return Collections.unmodifiableCollection(getVersionsImpl(gameVersion));
|
||||
}
|
||||
|
||||
@@ -95,11 +97,11 @@ public abstract class VersionList<T> {
|
||||
* @param remoteVersion the version of the remote version.
|
||||
* @return the specific remote version, null if it is not found.
|
||||
*/
|
||||
public final Optional<RemoteVersion<T>> getVersion(String gameVersion, String remoteVersion) {
|
||||
public final Optional<T> getVersion(String gameVersion, String remoteVersion) {
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
RemoteVersion<T> result = null;
|
||||
for (RemoteVersion<T> it : versions.get(gameVersion))
|
||||
T result = null;
|
||||
for (T it : versions.get(gameVersion))
|
||||
if (remoteVersion.equals(it.getSelfVersion()))
|
||||
result = it;
|
||||
return Optional.ofNullable(result);
|
||||
|
||||
@@ -19,7 +19,7 @@ package org.jackhuang.hmcl.download.forge;
|
||||
|
||||
import org.jackhuang.hmcl.download.RemoteVersion;
|
||||
|
||||
public class ForgeRemoteVersion extends RemoteVersion<Void> {
|
||||
public class ForgeRemoteVersion extends RemoteVersion {
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@@ -28,6 +28,6 @@ public class ForgeRemoteVersion extends RemoteVersion<Void> {
|
||||
* @param url the installer or universal jar URL.
|
||||
*/
|
||||
public ForgeRemoteVersion(String gameVersion, String selfVersion, String url) {
|
||||
super(gameVersion, selfVersion, url, null);
|
||||
super(gameVersion, selfVersion, url);
|
||||
}
|
||||
}
|
||||
@@ -32,13 +32,18 @@ import java.util.*;
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class ForgeVersionList extends VersionList<Void> {
|
||||
public final class ForgeVersionList extends VersionList<ForgeRemoteVersion> {
|
||||
|
||||
public static final ForgeVersionList INSTANCE = new ForgeVersionList();
|
||||
|
||||
private ForgeVersionList() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasType() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task refreshAsync(DownloadProvider downloadProvider) {
|
||||
final GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.injectURL(FORGE_LIST)));
|
||||
|
||||
@@ -17,12 +17,9 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.download.game;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import org.jackhuang.hmcl.download.RemoteVersion;
|
||||
import org.jackhuang.hmcl.game.ReleaseType;
|
||||
import org.jackhuang.hmcl.util.Constants;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.Validation;
|
||||
import org.jackhuang.hmcl.util.Immutable;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@@ -30,64 +27,31 @@ import java.util.Date;
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class GameRemoteVersion implements Validation {
|
||||
@Immutable
|
||||
public final class GameRemoteVersion extends RemoteVersion {
|
||||
|
||||
@SerializedName("id")
|
||||
private final String gameVersion;
|
||||
|
||||
@SerializedName("time")
|
||||
private final ReleaseType type;
|
||||
private final Date time;
|
||||
|
||||
@SerializedName("releaseTime")
|
||||
private final Date releaseTime;
|
||||
|
||||
@SerializedName("type")
|
||||
private final ReleaseType type;
|
||||
|
||||
@SerializedName("url")
|
||||
private final String url;
|
||||
|
||||
public GameRemoteVersion() {
|
||||
this("", new Date(), new Date(), ReleaseType.UNKNOWN);
|
||||
}
|
||||
|
||||
public GameRemoteVersion(String gameVersion, Date time, Date releaseTime, ReleaseType type) {
|
||||
this(gameVersion, time, releaseTime, type, Constants.DEFAULT_LIBRARY_URL + gameVersion + "/" + gameVersion + ".json");
|
||||
}
|
||||
|
||||
public GameRemoteVersion(String gameVersion, Date time, Date releaseTime, ReleaseType type, String url) {
|
||||
this.gameVersion = gameVersion;
|
||||
this.time = time;
|
||||
this.releaseTime = releaseTime;
|
||||
public GameRemoteVersion(String gameVersion, String selfVersion, String url, ReleaseType type, Date time) {
|
||||
super(gameVersion, selfVersion, url);
|
||||
this.type = type;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getGameVersion() {
|
||||
return gameVersion;
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public Date getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public Date getReleaseTime() {
|
||||
return releaseTime;
|
||||
}
|
||||
|
||||
public ReleaseType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate() throws JsonParseException {
|
||||
if (StringUtils.isBlank(gameVersion))
|
||||
throw new JsonParseException("GameRemoteVersion id cannot be blank");
|
||||
if (StringUtils.isBlank(url))
|
||||
throw new JsonParseException("GameRemoteVersion url cannot be blank");
|
||||
public int compareTo(RemoteVersion o) {
|
||||
if (!(o instanceof GameRemoteVersion))
|
||||
return 0;
|
||||
|
||||
return ((GameRemoteVersion) o).getTime().compareTo(getTime());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2018 huangyuhui <[email protected]>
|
||||
*
|
||||
* 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 {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.download.game;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import org.jackhuang.hmcl.game.ReleaseType;
|
||||
import org.jackhuang.hmcl.util.Constants;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.Validation;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class GameRemoteVersionInfo implements Validation {
|
||||
|
||||
@SerializedName("id")
|
||||
private final String gameVersion;
|
||||
|
||||
@SerializedName("time")
|
||||
private final Date time;
|
||||
|
||||
@SerializedName("releaseTime")
|
||||
private final Date releaseTime;
|
||||
|
||||
@SerializedName("type")
|
||||
private final ReleaseType type;
|
||||
|
||||
@SerializedName("url")
|
||||
private final String url;
|
||||
|
||||
public GameRemoteVersionInfo() {
|
||||
this("", new Date(), new Date(), ReleaseType.UNKNOWN);
|
||||
}
|
||||
|
||||
public GameRemoteVersionInfo(String gameVersion, Date time, Date releaseTime, ReleaseType type) {
|
||||
this(gameVersion, time, releaseTime, type, Constants.DEFAULT_LIBRARY_URL + gameVersion + "/" + gameVersion + ".json");
|
||||
}
|
||||
|
||||
public GameRemoteVersionInfo(String gameVersion, Date time, Date releaseTime, ReleaseType type, String url) {
|
||||
this.gameVersion = gameVersion;
|
||||
this.time = time;
|
||||
this.releaseTime = releaseTime;
|
||||
this.type = type;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getGameVersion() {
|
||||
return gameVersion;
|
||||
}
|
||||
|
||||
public Date getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public Date getReleaseTime() {
|
||||
return releaseTime;
|
||||
}
|
||||
|
||||
public ReleaseType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate() throws JsonParseException {
|
||||
if (StringUtils.isBlank(gameVersion))
|
||||
throw new JsonParseException("GameRemoteVersion id cannot be blank");
|
||||
if (StringUtils.isBlank(url))
|
||||
throw new JsonParseException("GameRemoteVersion url cannot be blank");
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2018 huangyuhui <[email protected]>
|
||||
*
|
||||
* 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 {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.download.game;
|
||||
|
||||
import org.jackhuang.hmcl.game.ReleaseType;
|
||||
import org.jackhuang.hmcl.util.Immutable;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
@Immutable
|
||||
public final class GameRemoteVersionTag {
|
||||
|
||||
private final ReleaseType type;
|
||||
private final Date time;
|
||||
|
||||
/**
|
||||
* No-arg constructor for Gson.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public GameRemoteVersionTag() {
|
||||
this(ReleaseType.UNKNOWN, new Date());
|
||||
}
|
||||
|
||||
public GameRemoteVersionTag(ReleaseType type, Date time) {
|
||||
this.type = type;
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public Date getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public ReleaseType getType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ import java.util.List;
|
||||
public final class GameRemoteVersions {
|
||||
|
||||
@SerializedName("versions")
|
||||
private final List<GameRemoteVersion> versions;
|
||||
private final List<GameRemoteVersionInfo> versions;
|
||||
|
||||
@SerializedName("latest")
|
||||
private final GameRemoteLatestVersions latest;
|
||||
@@ -44,7 +44,7 @@ public final class GameRemoteVersions {
|
||||
this(Collections.emptyList(), null);
|
||||
}
|
||||
|
||||
public GameRemoteVersions(List<GameRemoteVersion> versions, GameRemoteLatestVersions latest) {
|
||||
public GameRemoteVersions(List<GameRemoteVersionInfo> versions, GameRemoteLatestVersions latest) {
|
||||
this.versions = versions;
|
||||
this.latest = latest;
|
||||
}
|
||||
@@ -53,7 +53,7 @@ public final class GameRemoteVersions {
|
||||
return latest;
|
||||
}
|
||||
|
||||
public List<GameRemoteVersion> getVersions() {
|
||||
public List<GameRemoteVersionInfo> getVersions() {
|
||||
return versions;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,13 +32,18 @@ import java.util.Collections;
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class GameVersionList extends VersionList<GameRemoteVersionTag> {
|
||||
public final class GameVersionList extends VersionList<GameRemoteVersion> {
|
||||
|
||||
public static final GameVersionList INSTANCE = new GameVersionList();
|
||||
|
||||
private GameVersionList() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasType() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task refreshAsync(DownloadProvider downloadProvider) {
|
||||
GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.getVersionListURL()));
|
||||
@@ -56,12 +61,12 @@ public final class GameVersionList extends VersionList<GameRemoteVersionTag> {
|
||||
versions.clear();
|
||||
|
||||
GameRemoteVersions root = Constants.GSON.fromJson(task.getResult(), GameRemoteVersions.class);
|
||||
for (GameRemoteVersion remoteVersion : root.getVersions()) {
|
||||
versions.put(remoteVersion.getGameVersion(), new RemoteVersionGame(
|
||||
for (GameRemoteVersionInfo remoteVersion : root.getVersions()) {
|
||||
versions.put(remoteVersion.getGameVersion(), new GameRemoteVersion(
|
||||
remoteVersion.getGameVersion(),
|
||||
remoteVersion.getGameVersion(),
|
||||
remoteVersion.getUrl(),
|
||||
new GameRemoteVersionTag(remoteVersion.getType(), remoteVersion.getReleaseTime()))
|
||||
remoteVersion.getType(), remoteVersion.getReleaseTime())
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
@@ -70,15 +75,4 @@ public final class GameVersionList extends VersionList<GameRemoteVersionTag> {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static class RemoteVersionGame extends RemoteVersion<GameRemoteVersionTag> {
|
||||
public RemoteVersionGame(String gameVersion, String selfVersion, String url, GameRemoteVersionTag tag) {
|
||||
super(gameVersion, selfVersion, url, tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(RemoteVersion<GameRemoteVersionTag> o) {
|
||||
return o.getTag().getTime().compareTo(getTag().getTime());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,10 @@ import org.jackhuang.hmcl.util.NetworkUtils;
|
||||
import org.tukaani.xz.XZInputStream;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarInputStream;
|
||||
@@ -23,15 +26,15 @@ import static org.jackhuang.hmcl.util.DigestUtils.digest;
|
||||
import static org.jackhuang.hmcl.util.Hex.encodeHex;
|
||||
|
||||
public final class LibraryDownloadTask extends Task {
|
||||
private final FileDownloadTask xzTask;
|
||||
private final FileDownloadTask task;
|
||||
private final LibraryDownloadTaskHelper helperTask;
|
||||
private final File jar;
|
||||
private final File xzFile;
|
||||
private final Library library;
|
||||
|
||||
private boolean downloaded = false;
|
||||
private boolean xz;
|
||||
|
||||
public LibraryDownloadTask(AbstractDependencyManager dependencyManager, File file, Library library) {
|
||||
setSignificance(TaskSignificance.MODERATE);
|
||||
|
||||
if (library.is("net.minecraftforge", "forge"))
|
||||
library = library.setClassifier("universal");
|
||||
|
||||
@@ -42,20 +45,12 @@ public final class LibraryDownloadTask extends Task {
|
||||
|
||||
xzFile = new File(file.getAbsoluteFile().getParentFile(), file.getName() + ".pack.xz");
|
||||
|
||||
xzTask = new FileDownloadTask(NetworkUtils.toURL(url + ".pack.xz"),
|
||||
xzFile, null, 1);
|
||||
xzTask.setSignificance(TaskSignificance.MINOR);
|
||||
|
||||
setSignificance(TaskSignificance.MODERATE);
|
||||
|
||||
task = new FileDownloadTask(NetworkUtils.toURL(url),
|
||||
file,
|
||||
library.getDownload().getSha1() != null ? new IntegrityCheck("SHA-1", library.getDownload().getSha1()) : null);
|
||||
helperTask = new LibraryDownloadTaskHelper(file, url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends Task> getDependents() {
|
||||
return library.getChecksums() != null ? Collections.singleton(xzTask) : Collections.singleton(task);
|
||||
return Collections.singleton(helperTask);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,32 +61,75 @@ public final class LibraryDownloadTask extends Task {
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
if (!isDependentsSucceeded()) {
|
||||
// Since FileDownloadTask wraps the actual exception with another IOException.
|
||||
// We should extract it letting the error message clearer.
|
||||
Throwable t = library.getChecksums() != null ? xzTask.getLastException() : task.getLastException();
|
||||
if (t.getCause() != null && t.getCause() != t)
|
||||
throw new LibraryDownloadException(library, t.getCause());
|
||||
else
|
||||
throw new LibraryDownloadException(library, t);
|
||||
if (helperTask.task == null) {
|
||||
// NetworkUtils.URLExists failed.
|
||||
throw new LibraryDownloadException(library, helperTask.getLastException());
|
||||
} else {
|
||||
// Since FileDownloadTask wraps the actual exception with another IOException.
|
||||
// We should extract it letting the error message clearer.
|
||||
Throwable t = helperTask.task.getLastException();
|
||||
if (t.getCause() != null && t.getCause() != t)
|
||||
throw new LibraryDownloadException(library, t.getCause());
|
||||
else
|
||||
throw new LibraryDownloadException(library, t);
|
||||
}
|
||||
} else {
|
||||
if (xz) unpackLibrary(jar, FileUtils.readBytes(xzFile));
|
||||
if (!checksumValid(jar, library.getChecksums())) {
|
||||
jar.delete();
|
||||
throw new IOException("Checksum failed for " + library);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class LibraryDownloadTaskHelper extends Task {
|
||||
private FileDownloadTask task;
|
||||
private final File file;
|
||||
private final String url;
|
||||
|
||||
public LibraryDownloadTaskHelper(File file, String url) {
|
||||
this.file = file;
|
||||
this.url = url;
|
||||
|
||||
setName(library.getName());
|
||||
}
|
||||
|
||||
if (library.getChecksums() != null) {
|
||||
unpackLibrary(jar, FileUtils.readBytes(xzFile));
|
||||
if (!checksumValid(jar, library.getChecksums()))
|
||||
throw new IOException("Checksum failed for " + library);
|
||||
@Override
|
||||
public boolean isRelyingOnDependencies() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends Task> getDependencies() {
|
||||
return Collections.singleton(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
URL packXz = NetworkUtils.toURL(url + ".pack.xz");
|
||||
if (NetworkUtils.URLExists(packXz)) {
|
||||
task = new FileDownloadTask(packXz, xzFile, null);
|
||||
xz = true;
|
||||
} else {
|
||||
task = new FileDownloadTask(NetworkUtils.toURL(url),
|
||||
file,
|
||||
library.getDownload().getSha1() != null ? new IntegrityCheck("SHA-1", library.getDownload().getSha1()) : null);
|
||||
xz = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean checksumValid(File libPath, List<String> checksums) {
|
||||
try {
|
||||
if ((checksums == null) || (checksums.isEmpty())) {
|
||||
if (checksums == null || checksums.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
byte[] fileData = FileUtils.readBytes(libPath);
|
||||
boolean valid = checksums.contains(encodeHex(digest("SHA-1", fileData)));
|
||||
if ((!valid) && (libPath.getName().endsWith(".jar"))) {
|
||||
if (!valid && libPath.getName().endsWith(".jar")) {
|
||||
valid = validateJar(fileData, checksums);
|
||||
}
|
||||
return validateJar(fileData, checksums);
|
||||
return valid;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -115,8 +153,8 @@ public final class LibraryDownloadTask extends Task {
|
||||
}
|
||||
jar.close();
|
||||
if (hashes != null) {
|
||||
boolean passed = !checksums.contains(files.get("checksums.sha1"));
|
||||
if (passed) {
|
||||
boolean failed = !checksums.contains(files.get("checksums.sha1"));
|
||||
if (!failed) {
|
||||
for (String hash : hashes) {
|
||||
if ((!hash.trim().equals("")) && (hash.contains(" "))) {
|
||||
String[] e = hash.split(" ");
|
||||
@@ -125,17 +163,17 @@ public final class LibraryDownloadTask extends Task {
|
||||
String checksum = files.get(target);
|
||||
if ((!files.containsKey(target)) || (checksum == null)) {
|
||||
Logging.LOG.warning(" " + target + " : missing");
|
||||
passed = false;
|
||||
failed = true;
|
||||
break;
|
||||
} else if (!checksum.equals(validChecksum)) {
|
||||
Logging.LOG.warning(" " + target + " : failed (" + checksum + ", " + validChecksum + ")");
|
||||
passed = false;
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return passed;
|
||||
return !failed;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ public final class VersionJsonDownloadTask extends Task {
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
RemoteVersion<?> remoteVersion = gameVersionList.getVersions(gameVersion).stream().findFirst()
|
||||
RemoteVersion remoteVersion = gameVersionList.getVersions(gameVersion).stream().findFirst()
|
||||
.orElseThrow(() -> new IllegalStateException("Cannot find specific version " + gameVersion + " in remote repository"));
|
||||
String jsonURL = dependencyManager.getDownloadProvider().injectURL(remoteVersion.getUrl());
|
||||
dependencies.add(new GetTask(NetworkUtils.toURL(jsonURL), ID));
|
||||
|
||||
+6
-1
@@ -39,13 +39,18 @@ import java.util.Optional;
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class LiteLoaderBMCLVersionList extends VersionList<LiteLoaderRemoteVersionTag> {
|
||||
public final class LiteLoaderBMCLVersionList extends VersionList<LiteLoaderRemoteVersion> {
|
||||
|
||||
public static final LiteLoaderBMCLVersionList INSTANCE = new LiteLoaderBMCLVersionList();
|
||||
|
||||
private LiteLoaderBMCLVersionList() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasType() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task refreshAsync(DownloadProvider downloadProvider) {
|
||||
GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.injectURL(LITELOADER_LIST)));
|
||||
|
||||
+2
-2
@@ -74,7 +74,7 @@ public final class LiteLoaderInstallTask extends TaskResult<Version> {
|
||||
new LibrariesDownloadInfo(new LibraryDownloadInfo(null, remote.getUrl()))
|
||||
);
|
||||
|
||||
Version tempVersion = version.setLibraries(Lang.merge(remote.getTag().getLibraries(), Collections.singleton(library)));
|
||||
Version tempVersion = version.setLibraries(Lang.merge(remote.getLibraries(), Collections.singleton(library)));
|
||||
|
||||
String mcArg = version.getMinecraftArguments().orElse("");
|
||||
if (mcArg.contains("--tweakClass optifine.OptiFineTweaker"))
|
||||
@@ -84,7 +84,7 @@ public final class LiteLoaderInstallTask extends TaskResult<Version> {
|
||||
.setMainClass("net.minecraft.launchwrapper.Launch")
|
||||
.setLibraries(Lang.merge(tempVersion.getLibraries(), version.getLibraries()))
|
||||
.setLogging(Collections.emptyMap())
|
||||
.setMinecraftArguments(mcArg + " --tweakClass " + remote.getTag().getTweakClass())
|
||||
.setMinecraftArguments(mcArg + " --tweakClass " + remote.getTweakClass())
|
||||
//.setArguments(Arguments.addGameArguments(Lang.get(version.getArguments()), "--tweakClass", remote.getTag().getTweakClass()))
|
||||
);
|
||||
|
||||
|
||||
+16
-2
@@ -22,7 +22,9 @@ import org.jackhuang.hmcl.game.Library;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class LiteLoaderRemoteVersion extends RemoteVersion<LiteLoaderRemoteVersionTag> {
|
||||
public class LiteLoaderRemoteVersion extends RemoteVersion {
|
||||
private final String tweakClass;
|
||||
private final Collection<Library> libraries;
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@@ -31,6 +33,18 @@ public class LiteLoaderRemoteVersion extends RemoteVersion<LiteLoaderRemoteVersi
|
||||
* @param url the installer or universal jar URL.
|
||||
*/
|
||||
LiteLoaderRemoteVersion(String gameVersion, String selfVersion, String url, String tweakClass, Collection<Library> libraries) {
|
||||
super(gameVersion, selfVersion, url, new LiteLoaderRemoteVersionTag(tweakClass, libraries));
|
||||
super(gameVersion, selfVersion, url);
|
||||
|
||||
this.tweakClass = tweakClass;
|
||||
this.libraries = libraries;
|
||||
}
|
||||
|
||||
public Collection<Library> getLibraries() {
|
||||
return libraries;
|
||||
}
|
||||
|
||||
public String getTweakClass() {
|
||||
return tweakClass;
|
||||
}
|
||||
|
||||
}
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2018 huangyuhui <[email protected]>
|
||||
*
|
||||
* 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 {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.download.liteloader;
|
||||
|
||||
import org.jackhuang.hmcl.game.Library;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class LiteLoaderRemoteVersionTag {
|
||||
private final String tweakClass;
|
||||
private final Collection<Library> libraries;
|
||||
|
||||
/**
|
||||
* No-arg constructor for Gson.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public LiteLoaderRemoteVersionTag() {
|
||||
this("", Collections.emptySet());
|
||||
}
|
||||
|
||||
public LiteLoaderRemoteVersionTag(String tweakClass, Collection<Library> libraries) {
|
||||
this.tweakClass = tweakClass;
|
||||
this.libraries = libraries;
|
||||
}
|
||||
|
||||
public Collection<Library> getLibraries() {
|
||||
return libraries;
|
||||
}
|
||||
|
||||
public String getTweakClass() {
|
||||
return tweakClass;
|
||||
}
|
||||
|
||||
}
|
||||
+6
-1
@@ -39,13 +39,18 @@ import java.util.Optional;
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class LiteLoaderVersionList extends VersionList<LiteLoaderRemoteVersionTag> {
|
||||
public final class LiteLoaderVersionList extends VersionList<LiteLoaderRemoteVersion> {
|
||||
|
||||
public static final LiteLoaderVersionList INSTANCE = new LiteLoaderVersionList();
|
||||
|
||||
private LiteLoaderVersionList() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasType() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task refreshAsync(DownloadProvider downloadProvider) {
|
||||
GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.injectURL(LITELOADER_LIST)));
|
||||
|
||||
+9
-4
@@ -33,13 +33,18 @@ import java.util.*;
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class OptiFineBMCLVersionList extends VersionList<Void> {
|
||||
public final class OptiFineBMCLVersionList extends VersionList<OptiFineRemoteVersion> {
|
||||
|
||||
public static final OptiFineBMCLVersionList INSTANCE = new OptiFineBMCLVersionList();
|
||||
|
||||
private OptiFineBMCLVersionList() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasType() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task refreshAsync(DownloadProvider downloadProvider) {
|
||||
GetTask task = new GetTask(NetworkUtils.toURL("http://bmclapi2.bangbang93.com/optifine/versionlist"));
|
||||
@@ -57,16 +62,16 @@ public final class OptiFineBMCLVersionList extends VersionList<Void> {
|
||||
}.getType());
|
||||
for (OptiFineVersion element : root) {
|
||||
String version = element.getType() + "_" + element.getPatch();
|
||||
if (element.getType() == null || "pre".equals(element.getPatch()))
|
||||
continue;
|
||||
String mirror = "http://bmclapi2.bangbang93.com/optifine/" + element.getGameVersion() + "/" + element.getType() + "/" + element.getPatch();
|
||||
if (!duplicates.add(mirror))
|
||||
continue;
|
||||
|
||||
boolean isPre = element.getPatch() != null && (element.getPatch().equals("pre") || element.getPatch().startsWith("alpha"));
|
||||
|
||||
if (StringUtils.isBlank(element.getGameVersion()))
|
||||
continue;
|
||||
VersionNumber.parseVersion(element.getGameVersion())
|
||||
.ifPresent(gameVersion -> versions.put(gameVersion, new OptiFineRemoteVersion(gameVersion, version, () -> mirror)));
|
||||
.ifPresent(gameVersion -> versions.put(gameVersion, new OptiFineRemoteVersion(gameVersion, version, () -> mirror, isPre)));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
+3
-3
@@ -21,11 +21,11 @@ import org.jackhuang.hmcl.download.RemoteVersion;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class OptiFineRemoteVersion extends RemoteVersion<Void> {
|
||||
public class OptiFineRemoteVersion extends RemoteVersion {
|
||||
private final Supplier<String> url;
|
||||
|
||||
public OptiFineRemoteVersion(String gameVersion, String selfVersion, Supplier<String> url) {
|
||||
super(gameVersion, selfVersion, "", null);
|
||||
public OptiFineRemoteVersion(String gameVersion, String selfVersion, Supplier<String> url, boolean snapshot) {
|
||||
super(gameVersion, selfVersion, "", snapshot ? Type.SNAPSHOT : Type.RELEASE);
|
||||
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2018 huangyuhui <[email protected]>
|
||||
*
|
||||
* 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 {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.download.optifine;
|
||||
|
||||
import org.jackhuang.hmcl.download.DownloadProvider;
|
||||
import org.jackhuang.hmcl.download.VersionList;
|
||||
import org.jackhuang.hmcl.task.GetTask;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class OptiFineVersionList extends VersionList<Void> {
|
||||
|
||||
private static final Pattern PATTERN = Pattern.compile("OptiFine (.*?) ");
|
||||
private static final Pattern LINK_PATTERN = Pattern.compile("\"downloadx\\?f=OptiFine(.*)\"");
|
||||
|
||||
public static final OptiFineVersionList INSTANCE = new OptiFineVersionList();
|
||||
|
||||
private OptiFineVersionList() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task refreshAsync(DownloadProvider downloadProvider) {
|
||||
GetTask task = new GetTask(NetworkUtils.toURL("https://optifine.net/downloads"));
|
||||
return new Task() {
|
||||
@Override
|
||||
public Collection<Task> getDependents() {
|
||||
return Collections.singleton(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
lock.writeLock().lock();
|
||||
|
||||
try {
|
||||
versions.clear();
|
||||
|
||||
String html = task.getResult().replace(" ", " ").replace(">", ">").replace("<", "<").replace("<br>", "<br />");
|
||||
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder db = factory.newDocumentBuilder();
|
||||
Document doc = db.parse(new ByteArrayInputStream(html.getBytes("UTF-8")));
|
||||
Element r = doc.getDocumentElement();
|
||||
NodeList tables = r.getElementsByTagName("table");
|
||||
for (int i = 0; i < tables.getLength(); i++) {
|
||||
Element e = (Element) tables.item(i);
|
||||
if ("downloadTable".equals(e.getAttribute("class"))) {
|
||||
NodeList tr = e.getElementsByTagName("tr");
|
||||
for (int k = 0; k < tr.getLength(); k++) {
|
||||
NodeList downloadLine = ((Element) tr.item(k)).getElementsByTagName("td");
|
||||
String url = null, version = null, gameVersion = null;
|
||||
for (int j = 0; j < downloadLine.getLength(); j++) {
|
||||
Element td = (Element) downloadLine.item(j);
|
||||
if (td.getAttribute("class") != null && td.getAttribute("class").startsWith("downloadLineMirror"))
|
||||
url = ((Element) td.getElementsByTagName("a").item(0)).getAttribute("href");
|
||||
if (td.getAttribute("class") != null && td.getAttribute("class").startsWith("downloadLineFile"))
|
||||
version = td.getTextContent();
|
||||
}
|
||||
if (version == null || url == null)
|
||||
continue;
|
||||
|
||||
Matcher matcher = PATTERN.matcher(version);
|
||||
while (matcher.find())
|
||||
gameVersion = matcher.group(1);
|
||||
if (gameVersion == null)
|
||||
continue;
|
||||
|
||||
String finalURL = url;
|
||||
versions.put(gameVersion, new OptiFineRemoteVersion(gameVersion, version, Lang.hideException(() -> getLink(finalURL))));
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static String getLink(String url) throws IOException {
|
||||
String result = null;
|
||||
String content = NetworkUtils.doGet(NetworkUtils.toURL(url));
|
||||
Matcher m = LINK_PATTERN.matcher(content);
|
||||
while (m.find())
|
||||
result = m.group(1);
|
||||
if (result == null)
|
||||
throw new IllegalStateException("Cannot find version in " + content);
|
||||
return "https://optifine.net/downloadx?f=OptiFine" + result;
|
||||
}
|
||||
}
|
||||
@@ -108,7 +108,7 @@ public abstract class Task {
|
||||
return true;
|
||||
}
|
||||
|
||||
private String name = getClass().toString();
|
||||
private String name = getClass().getName();
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
|
||||
@@ -17,10 +17,7 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
@@ -156,4 +153,12 @@ public final class NetworkUtils {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean URLExists(URL url) throws IOException {
|
||||
try (InputStream stream = url.openStream()) {
|
||||
return true;
|
||||
} catch (FileNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user