Support fabric detection

This commit is contained in:
huanghongxun
2019-04-28 14:17:28 +08:00
parent 974cba8dee
commit e0abf3e0c1
13 changed files with 268 additions and 177 deletions

View File

@@ -20,59 +20,68 @@ package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.Version;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;
public final class LibraryAnalyzer {
private final Library forge;
private final Library liteLoader;
private final Library optiFine;
private final Map<LibraryType, Library> libraries;
public LibraryAnalyzer(Library forge, Library liteLoader, Library optiFine) {
this.forge = forge;
this.liteLoader = liteLoader;
this.optiFine = optiFine;
private LibraryAnalyzer(Map<LibraryType, Library> libraries) {
this.libraries = libraries;
}
public Optional<Library> getForge() {
return Optional.ofNullable(forge);
public Optional<Library> get(LibraryType type) {
return Optional.ofNullable(libraries.get(type));
}
public boolean hasForge() {
return forge != null;
public boolean has(LibraryType type) {
return libraries.containsKey(type);
}
public Optional<Library> getLiteLoader() {
return Optional.ofNullable(liteLoader);
}
public boolean hasLiteLoader() {
return liteLoader != null;
}
public Optional<Library> getOptiFine() {
return Optional.ofNullable(optiFine);
}
public boolean hasOptiFine() {
return optiFine != null;
public boolean hasModLoader() {
return Arrays.stream(LibraryType.values())
.filter(LibraryType::isModLoader)
.anyMatch(this::has);
}
public static LibraryAnalyzer analyze(Version version) {
Library forge = null, liteLoader = null, optiFine = null;
Map<LibraryType, Library> libraries = new EnumMap<>(LibraryType.class);
for (Library library : version.getLibraries()) {
String groupId = library.getGroupId();
String artifactId = library.getArtifactId();
if (groupId.equalsIgnoreCase("net.minecraftforge") && artifactId.equalsIgnoreCase("forge"))
forge = library;
if (groupId.equalsIgnoreCase("com.mumfrey") && artifactId.equalsIgnoreCase("liteloader"))
liteLoader = library;
if ((groupId.equalsIgnoreCase("optifine") || groupId.equalsIgnoreCase("net.optifine")) && artifactId.equalsIgnoreCase("optifine"))
optiFine = library;
for (LibraryType type : LibraryType.values()) {
if (type.group.matcher(groupId).matches() && type.artifact.matcher(artifactId).matches()) {
libraries.put(type, library);
break;
}
}
}
return new LibraryAnalyzer(forge, liteLoader, optiFine);
return new LibraryAnalyzer(libraries);
}
public enum LibraryType {
FORGE(true, Pattern.compile("net\\.minecraftforge"), Pattern.compile("forge")),
LITELOADER(true, Pattern.compile("com\\.mumfrey"), Pattern.compile("liteloader")),
OPTIFINE(false, Pattern.compile("(net\\.)?optifine"), Pattern.compile(".*")),
FABRIC(true, Pattern.compile("net\\.fabricmc"), Pattern.compile(".*"));
private final Pattern group, artifact;
private final boolean modLoader;
LibraryType(boolean modLoader, Pattern group, Pattern artifact) {
this.modLoader = modLoader;
this.group = group;
this.artifact = artifact;
}
public boolean isModLoader() {
return modLoader;
}
}
}

View File

@@ -20,6 +20,8 @@ package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.game.*;
import org.jackhuang.hmcl.task.TaskResult;
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*;
public class MaintainTask extends TaskResult<Version> {
private final Version version;
@@ -47,20 +49,20 @@ public class MaintainTask extends TaskResult<Version> {
LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version);
VersionLibraryBuilder builder = new VersionLibraryBuilder(version);
if (!libraryAnalyzer.hasForge()) {
if (!libraryAnalyzer.has(FORGE)) {
builder.removeTweakClass("forge");
}
// Installing Forge will override the Minecraft arguments in json, so LiteLoader and OptiFine Tweaker are being re-added.
builder.removeTweakClass("liteloader");
if (libraryAnalyzer.hasLiteLoader()) {
if (libraryAnalyzer.has(LITELOADER)) {
builder.addArgument("--tweakClass", "com.mumfrey.liteloader.launch.LiteLoaderTweaker");
}
builder.removeTweakClass("optifine");
if (libraryAnalyzer.hasOptiFine()) {
if (!libraryAnalyzer.hasLiteLoader() && !libraryAnalyzer.hasForge()) {
if (libraryAnalyzer.has(OPTIFINE)) {
if (!libraryAnalyzer.has(LITELOADER) && !libraryAnalyzer.has(FORGE)) {
builder.addArgument("--tweakClass", "optifine.OptiFineTweaker");
} else {
// If forge or LiteLoader installed, OptiFine Forge Tweaker is needed.

View File

@@ -25,7 +25,9 @@ import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.StringUtils;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
@@ -66,7 +68,9 @@ public final class OptiFineInstallTask extends TaskResult<Version> {
@Override
public void execute() {
if ("cpw.mods.modlauncher.Launcher".equals(version.getMainClass()))
if (!Arrays.asList("net.minecraft.client.main.Main",
"net.minecraft.launchwrapper.Launch")
.contains(version.getMainClass()))
throw new UnsupportedOptiFineInstallationException();
String remoteVersion = remote.getGameVersion() + "_" + remote.getSelfVersion();

View File

@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.mod;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.SerializedName;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
@@ -27,7 +26,6 @@ import javafx.collections.ObservableList;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.gson.Validation;
import org.jackhuang.hmcl.util.io.CompressingUtils;
import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.io.Unzipper;
@@ -254,54 +252,6 @@ public class Datapack {
}
}
private static class PackMcMeta implements Validation {
@SerializedName("pack")
private final PackInfo pack;
public PackMcMeta() {
this(new PackInfo());
}
public PackMcMeta(PackInfo packInfo) {
this.pack = packInfo;
}
public PackInfo getPackInfo() {
return pack;
}
@Override
public void validate() throws JsonParseException {
if (pack == null)
throw new JsonParseException("pack cannot be null");
}
public static class PackInfo {
@SerializedName("pack_format")
private final int packFormat;
@SerializedName("description")
private final String description;
public PackInfo() {
this(0, "");
}
public PackInfo(int packFormat, String description) {
this.packFormat = packFormat;
this.description = description;
}
public int getPackFormat() {
return packFormat;
}
public String getDescription() {
return description;
}
}
}
private static final String DISABLED_EXT = "disabled";
}

View File

@@ -0,0 +1,94 @@
/*
* 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;
import com.google.gson.*;
import com.google.gson.annotations.JsonAdapter;
import org.jackhuang.hmcl.util.Immutable;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.CompressingUtils;
import org.jackhuang.hmcl.util.io.IOUtils;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Immutable
public final class FabricModMetadata {
private final String name;
private final String version;
private final String description;
private final List<FabricModAuthor> authors;
private final Map<String, String> contact;
public FabricModMetadata() {
this("", "", "", Collections.emptyList(), Collections.emptyMap());
}
public FabricModMetadata(String name, String version, String description, List<FabricModAuthor> authors, Map<String, String> contact) {
this.name = name;
this.version = version;
this.description = description;
this.authors = authors;
this.contact = contact;
}
public static ModInfo fromFile(ModManager modManager, File modFile) throws IOException, JsonParseException {
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile.toPath())) {
Path mcmod = fs.getPath("fabric.mod.json");
if (Files.notExists(mcmod))
throw new IOException("File " + modFile + " is not a Fabric mod.");
FabricModMetadata metadata = JsonUtils.fromNonNullJson(IOUtils.readFullyAsString(Files.newInputStream(mcmod)), FabricModMetadata.class);
String authors = metadata.authors == null ? "" : metadata.authors.stream().map(author -> author.name).collect(Collectors.joining(", "));
return new ModInfo(modManager, modFile, metadata.name, metadata.description,
authors, metadata.version, "", metadata.contact != null ? metadata.contact.getOrDefault("homepage", "") : "");
}
}
@JsonAdapter(FabricModAuthorSerializer.class)
public static final class FabricModAuthor {
private final String name;
public FabricModAuthor() {
this("");
}
public FabricModAuthor(String name) {
this.name = name;
}
}
public static final class FabricModAuthorSerializer implements JsonSerializer<FabricModAuthor>, JsonDeserializer<FabricModAuthor> {
@Override
public FabricModAuthor deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return json.isJsonPrimitive() ? new FabricModAuthor(json.getAsString()) : new FabricModAuthor(json.getAsJsonObject().getAsJsonPrimitive("name").getAsString());
}
@Override
public JsonElement serialize(FabricModAuthor src, Type typeOfSrc, JsonSerializationContext context) {
return src == null ? JsonNull.INSTANCE : new JsonPrimitive(src.name);
}
}
}

View File

@@ -64,7 +64,12 @@ public final class ModManager {
}
try {
return RiftModMetadata.fromFile(this, modFile);
return FabricModMetadata.fromFile(this, modFile);
} catch (Exception ignore) {
}
try {
return PackMcMeta.fromFile(this, modFile);
} catch (Exception ignore) {
}

View File

@@ -0,0 +1,98 @@
/*
* 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;
import com.google.gson.*;
import com.google.gson.annotations.JsonAdapter;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.util.Immutable;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.gson.Validation;
import org.jackhuang.hmcl.util.io.CompressingUtils;
import org.jackhuang.hmcl.util.io.IOUtils;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Immutable
public class PackMcMeta implements Validation {
@SerializedName("pack")
private final PackInfo pack;
public PackMcMeta() {
this(new PackInfo());
}
public PackMcMeta(PackInfo packInfo) {
this.pack = packInfo;
}
public PackInfo getPackInfo() {
return pack;
}
@Override
public void validate() throws JsonParseException {
if (pack == null)
throw new JsonParseException("pack cannot be null");
}
public static class PackInfo {
@SerializedName("pack_format")
private final int packFormat;
@SerializedName("description")
private final String description;
public PackInfo() {
this(0, "");
}
public PackInfo(int packFormat, String description) {
this.packFormat = packFormat;
this.description = description;
}
public int getPackFormat() {
return packFormat;
}
public String getDescription() {
return description;
}
}
public static ModInfo fromFile(ModManager modManager, File modFile) throws IOException, JsonParseException {
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile.toPath())) {
Path mcmod = fs.getPath("pack.mcmeta");
if (Files.notExists(mcmod))
throw new IOException("File " + modFile + " is not a resource pack.");
PackMcMeta metadata = JsonUtils.fromNonNullJson(IOUtils.readFullyAsString(Files.newInputStream(mcmod)), PackMcMeta.class);
return new ModInfo(modManager, modFile, metadata.pack.description, "", "", "", "", "");
}
}
}

View File

@@ -1,74 +0,0 @@
/*
* 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;
import com.google.gson.JsonParseException;
import org.jackhuang.hmcl.util.Immutable;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.CompressingUtils;
import org.jackhuang.hmcl.util.io.IOUtils;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
@Immutable
public final class RiftModMetadata {
private final String id;
private final String name;
private final List<String> authors;
public RiftModMetadata() {
this("", "", Collections.emptyList());
}
public RiftModMetadata(String id, String name, List<String> authors) {
this.id = id;
this.name = name;
this.authors = authors;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public List<String> getAuthors() {
return authors;
}
public static ModInfo fromFile(ModManager modManager, File modFile) throws IOException, JsonParseException {
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile.toPath())) {
Path mcmod = fs.getPath("riftmod.json");
if (Files.notExists(mcmod))
throw new IOException("File " + modFile + " is not a Forge mod.");
RiftModMetadata metadata = JsonUtils.fromNonNullJson(IOUtils.readFullyAsString(Files.newInputStream(mcmod)), RiftModMetadata.class);
String authors = metadata.getAuthors() == null ? "" : String.join(", ", metadata.getAuthors());
return new ModInfo(modManager, modFile, metadata.getName(), "",
authors, "", "", "");
}
}
}