add: allow installing OptiFine 1.14.4 and Forge in same game version

This commit is contained in:
huanghongxun 2020-05-03 19:21:54 +08:00
parent b53a3ebcc2
commit 7c510f9fb8
26 changed files with 176 additions and 27 deletions

3
.gitignore vendored
View File

@ -17,17 +17,20 @@ NVIDIA
/build/
/HMCL/build/
/HMCLCore/build/
/HMCLTransformerDiscoveryService/build/
# idea
.idea
/out/
/HMCL/out/
/HMCLCore/out/
/HMCLTransformerDiscoveryService/out/
# eclipse
/bin/
/HMCL/bin/
/HMCLCore/bin/
/HMCLTransformerDiscoveryService/bin/
.classpath
.project
.settings

View File

@ -1,3 +1,9 @@
buildscript {
dependencies {
classpath 'org.tukaani:xz:1.8'
}
}
plugins {
id 'com.github.johnrengelman.shadow' version '4.0.0'
id 'application'
@ -77,7 +83,7 @@ def repack(File file) {
jar {
manifest {
attributes 'Created-By': 'Copyright(c) 2013-2018 huangyuhui.',
attributes 'Created-By': 'Copyright(c) 2013-2020 huangyuhui.',
'Main-Class': mainClassName,
'Multi-Release': 'true',
'Implementation-Version': version

View File

@ -20,6 +20,7 @@ package org.jackhuang.hmcl.game;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import javafx.scene.image.Image;
import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.mod.Modpack;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.VersionSetting;
@ -218,9 +219,9 @@ public class HMCLGameRepository extends DefaultGameRepository {
if (iconFile.exists())
return new Image("file:" + iconFile.getAbsolutePath());
else if (version.getMainClass() != null &&
("net.minecraft.launchwrapper.Launch".equals(version.getMainClass())
(LibraryAnalyzer.LAUNCH_WRAPPER_MAIN.equals(version.getMainClass())
|| version.getMainClass().startsWith("net.fabricmc")
|| "cpw.mods.modlauncher.Launcher".equals(version.getMainClass())))
|| LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(version.getMainClass())))
return newImage("/assets/img/furnace.png");
else
return newImage("/assets/img/grass.png");

View File

@ -23,6 +23,7 @@ import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.auth.*;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorDownloadException;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.download.MaintainTask;
import org.jackhuang.hmcl.download.game.GameAssetIndexDownloadTask;
import org.jackhuang.hmcl.download.game.LibraryDownloadException;
@ -330,7 +331,7 @@ public final class LauncherHelper {
// LaunchWrapper 1.12 will crash because of assuming the system class loader is an instance of URLClassLoader.
if (!flag && java.getParsedVersion() >= JavaVersion.JAVA_9_AND_LATER
&& version.getMainClass().contains("launchwrapper")
&& version.getMainClass().equals(LibraryAnalyzer.LAUNCH_WRAPPER_MAIN)
&& version.getLibraries().stream()
.filter(library -> "launchwrapper".equals(library.getArtifactId()))
.anyMatch(library -> VersionNumber.asVersion(library.getVersion()).compareTo(VersionNumber.asVersion("1.13")) < 0)) {
@ -421,6 +422,18 @@ public final class LauncherHelper {
}
}
// OptiFine 1.14.4 is not compatible with Forge 28.2.2 and later versions.
if (!flag) {
boolean hasForge28_2_2 = version.getLibraries().stream().filter(it -> it.is("net.minecraftforge", "forge"))
.anyMatch(it ->
VersionNumber.VERSION_COMPARATOR.compare("1.14.4-28.2.2", it.getVersion()) <= 0);
boolean hasOptiFine = version.getLibraries().stream().anyMatch(it -> it.is("optifine", "OptiFine"));
if (hasForge28_2_2 && hasOptiFine && gameVersion.compareTo(VersionNumber.asVersion("1.14.4")) == 0) {
Controllers.confirm(i18n("launch.advice.forge28_2_2_optifine"), i18n("message.error"), onAccept, null);
flag = true;
}
}
if (!flag)
onAccept.run();
}

View File

@ -169,6 +169,7 @@ launch.advice.corrected=We have already fixed the JVM selection. If you want to
launch.advice.uncorrected=If you are sure that the game can be started normally, you can disable the Java VM check in game settings.
launch.advice.different_platform=Your OS is 64-Bit but your Java is 32-Bit. The 64-Bit Java is recommended.
launch.advice.forge2760_liteloader=Forge 2760 and higher are not compatible with LiteLoader, please consider upgrading Forge to 2773 or later. Shall we continue launching?
launch.advice.forge28_2_2_optifine=Forge 28.2.2 and later versions are not compatible with OptiFine. Please consider downgrading Forge to 28.2.1 or earlier versions. Shall we continue launching?
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 or earlier with Java 9 or later versions of Java.

View File

@ -165,6 +165,7 @@ launch.advice.corrected=Ya hemos corregido la selección de JVM y el juego puede
launch.advice.uncorrected=Si está seguro que el juego puede ser lanzado normalmente, puede deshabilitar el Java VM checker en la configuración del juego.
launch.advice.different_platform=Su SO es 64-Bit pero su Java es 32-Bit. 64-Bit Java es recomendado.
launch.advice.forge2760_liteloader=Forge 2760 y posterior no es compatible con LiteLoader, considere actualizando Forge a 2773 o más.
launch.advice.forge28_2_2_optifine=Forge 28.2.2 and later versions are not compatible with OptiFine. Please consider downgrading Forge to 28.2.1 or earlier versions. Shall we continue launching?
launch.advice.java8_1_13=Minecraft 1.13 y posterior sólo pueden correr en Java 8 o luego.
launch.advice.java8_51_1_13=Minecraft 1.13 puediera fallar en Java 8 anterior 1.8.0_51. Debe instalar la versión de Java 8 más reciente.
launch.advice.java9=No puedes lanzar Minecraft hasta que su versión de juego sea más allá de 1.13 con Java 9 o posterior.

View File

@ -169,6 +169,7 @@ launch.advice.corrected=Мы уже исправили выбор JVMЕ. Есл
launch.advice.uncorrected=Если вы уверены, что игра запускается нормально, то можете отключить проверку Java VM в настройках игры.
launch.advice.different_platform=Ваша ОС 64-битная, а ваша Java 32-битная. Рекомендуется использовать 64-битный Java.
launch.advice.forge2760_liteloader=Forge 2760 и выше не совместимы с LiteLoader, рассмотрите возможность обновления Forge до 2773 или более поздней версии.
launch.advice.forge28_2_2_optifine=Forge 28.2.2 and later versions are not compatible with OptiFine. Please consider downgrading Forge to 28.2.1 or earlier versions. Shall we continue launching?
launch.advice.java8_1_13=Minecraft 1.13 и выше может работать только на Java 8 или более поздней версии.
launch.advice.java8_51_1_13=У Minecraft 1.13 может произойти сбой на Java 8 меньше, чем 1.8.0_51. Установите последнюю версию Java 8.
launch.advice.java9=Вы не можете запустить Minecraft 1.12 или более ранней версии с Java 9 или более новых версий Java.

View File

@ -169,6 +169,7 @@ launch.advice.corrected=我們已經修正了問題。如果您確實希望使
launch.advice.uncorrected=如果您確實希望使用您自訂的 Java 虛擬機,您可以在遊戲設定中關閉 Java 虛擬機相容性檢查。
launch.advice.different_platform=您的系統為 64 位元,但是 Java 是 32 位元,建議您安裝 64 位 Java。
launch.advice.forge2760_liteloader=Forge 2760 與 LiteLoader 不相容。請刪除 LiteLoader 或者變更 Forge 至 2773 或更新的版本。是否繼續啟動?
launch.advice.forge28_2_2_optifine=Forge 28.2.2 或更高版本與 OptiFine 不相容。請變更 Forge 至 28.2.1 或更低版本。是否繼續啟動?
launch.advice.java8_1_13=Minecraft 1.13 只支援 Java 8 或更高版本,請使用 Java 8 或最新版本。
launch.advice.java8_51_1_13=低於 1.8.0_51 的 Java 版本可能會導致 Minecraft 1.13 崩潰。建議您到 https://java.com 安裝最新版的 Java 8。
launch.advice.java9=低於 (包含) 1.13 的有安裝 Mod 的 Minecraft 版本不支援 Java 9 或更高版本,請使用 Java 8。

View File

@ -169,6 +169,7 @@ launch.advice.corrected=我们已经修复了问题。如果您确实希望使
launch.advice.uncorrected=如果您确实希望使用您自定义的 Java 虚拟机,您可以在游戏设置中关闭 Java 虚拟机兼容性检查。
launch.advice.different_platform=您的系统是 64 位,但是 Java 是 32 位的,建议您安装 64 位 Java。
launch.advice.forge2760_liteloader=Forge 2760 与 LiteLoader 不兼容。请删除 LiteLoader 或者更换 Forge 至 2773 或更新的版本。是否继续启动?
launch.advice.forge28_2_2_optifine=Forge 28.2.2 或更高版本与 OptiFine 不兼容。请更换 Forge 至 28.2.1 或更低版本。是否继续启动?
launch.advice.java8_1_13=Minecraft 1.13 只支持 Java 8 或更高版本,请使用 Java 8 或最新版本。
launch.advice.java8_51_1_13=低于 1.8.0_51 的 Java 版本可能会导致 Minecraft 1.13 崩溃。建议您到 https://java.com 安装最新版的 Java 8。
launch.advice.java9=低于 1.13 的有安装 Mod 的 Minecraft 版本不支持 Java 9 或更高版本,请使用 Java 8。

View File

@ -198,4 +198,8 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
return libraryVersion;
}
}
public static final String VANILLA_MAIN = "net.minecraft.client.main.Main";
public static final String LAUNCH_WRAPPER_MAIN = "net.minecraft.launchwrapper.Launch";
public static final String MOD_LAUNCHER_MAIN = "cpw.mods.modlauncher.Launcher";
}

View File

@ -24,18 +24,22 @@ import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.game.VersionLibraryBuilder;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.SimpleMultimap;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.versioning.VersionNumber;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.logging.Level;
import java.util.stream.Collectors;
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*;
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.FORGE;
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.LITELOADER;
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.OPTIFINE;
public class MaintainTask extends Task<Version> {
private final GameRepository repository;
@ -60,13 +64,15 @@ public class MaintainTask extends Task<Version> {
String mainClass = version.resolve(null).getMainClass();
if (mainClass != null && mainClass.contains("launchwrapper")) {
return maintainOptiFineLibrary(repository, maintainGameWithLaunchWrapper(unique(version), true));
if (mainClass != null && mainClass.equals(LibraryAnalyzer.LAUNCH_WRAPPER_MAIN)) {
return maintainOptiFineLibrary(repository, maintainGameWithLaunchWrapper(unique(version), true), false);
} else if (mainClass != null && mainClass.equals(LibraryAnalyzer.MOD_LAUNCHER_MAIN)) {
// Forge 1.13 and OptiFine
return maintainOptiFineLibrary(repository, maintainGameWithModLauncher(repository, unique(version)), true);
} else {
// Vanilla Minecraft does not need maintain
// Forge 1.13 support not implemented, not compatible with OptiFine currently.
// Fabric does not need maintain, nothing compatible with fabric now.
return maintainOptiFineLibrary(repository, unique(version));
return maintainOptiFineLibrary(repository, unique(version), false);
}
}
@ -116,7 +122,34 @@ public class MaintainTask extends Task<Version> {
return mainClass == null ? ret : ret.setMainClass(mainClass);
}
private static Version maintainOptiFineLibrary(GameRepository repository, Version version) {
private static Version maintainGameWithModLauncher(GameRepository repository, Version version) {
LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version);
VersionLibraryBuilder builder = new VersionLibraryBuilder(version);
if (!libraryAnalyzer.has(FORGE)) return version;
if (libraryAnalyzer.has(OPTIFINE)) {
Library hmclTransformerDiscoveryService = new Library(new Artifact("org.jackhuang.hmcl", "transformer-discovery-service", "1.0"));
Optional<Library> optiFine = version.getLibraries().stream().filter(library -> library.is("optifine", "OptiFine")).findAny();
boolean libraryExisting = version.getLibraries().stream().anyMatch(library -> library.is("org.jackhuang.hmcl", "transformer-discovery-service"));
optiFine.ifPresent(library -> {
builder.addJvmArgument("-Dhmcl.transformer.candidates=${libraries_directory}/" + library.getPath());
if (!libraryExisting) builder.addLibrary(hmclTransformerDiscoveryService);
Path libraryPath = repository.getLibraryFile(version, hmclTransformerDiscoveryService).toPath();
try {
Files.createDirectories(libraryPath.getParent());
Files.copy(MaintainTask.class.getResourceAsStream("/assets/game/HMCLTransformerDiscoveryService-1.0.jar"),
libraryPath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
Logging.LOG.log(Level.WARNING, "Unable to unpack HMCLTransformerDiscoveryService", e);
}
});
}
return builder.build();
}
private static Version maintainOptiFineLibrary(GameRepository repository, Version version, boolean remove) {
LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version);
List<Library> libraries = new ArrayList<>(version.getLibraries());
@ -135,7 +168,7 @@ public class MaintainTask extends Task<Version> {
// Although we have altered priority of OptiFine higher than Forge,
// there still exists a situation that Forge is installed without patch.
// Here we manually alter the position of OptiFine library in classpath.
libraries.add(newLibrary);
if (!remove) libraries.add(newLibrary);
}
}

View File

@ -19,6 +19,7 @@ package org.jackhuang.hmcl.download.forge;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.DependencyManager;
import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.download.VersionMismatchException;
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
import org.jackhuang.hmcl.game.GameVersion;
@ -104,7 +105,7 @@ public final class ForgeInstallTask extends Task<Version> {
String originalMainClass = version.resolve(dependencyManager.getGameRepository()).getMainClass();
if (VersionNumber.VERSION_COMPARATOR.compare("1.13", remote.getGameVersion()) <= 0) {
// Forge 1.13 is not compatible with any other libraries.
if (!"net.minecraft.client.main.Main".equals(originalMainClass) && !"cpw.mods.modlauncher.Launcher".equals(originalMainClass))
if (!LibraryAnalyzer.VANILLA_MAIN.equals(originalMainClass) && !LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(originalMainClass) && !LibraryAnalyzer.LAUNCH_WRAPPER_MAIN.equals(originalMainClass))
throw new OptiFineInstallTask.UnsupportedOptiFineInstallationException();
} else {
// Forge 1.12 and older versions is compatible with vanilla and launchwrapper.

View File

@ -69,7 +69,7 @@ public final class LiteLoaderInstallTask extends Task<Version> {
remote.getSelfVersion(),
60000,
new Arguments().addGameArguments("--tweakClass", "com.mumfrey.liteloader.launch.LiteLoaderTweaker"),
"net.minecraft.launchwrapper.Launch",
LibraryAnalyzer.LAUNCH_WRAPPER_MAIN,
Lang.merge(remote.getLibraries(), Collections.singleton(library)))
.setLogging(Collections.emptyMap()) // Mods may log in malformed format, causing XML parser to crash. So we suppress using official log4j configuration
);

View File

@ -121,7 +121,7 @@ public final class OptiFineInstallTask extends Task<Version> {
@Override
public void execute() throws Exception {
String originalMainClass = version.resolve(dependencyManager.getGameRepository()).getMainClass();
if (!"net.minecraft.client.main.Main".equals(originalMainClass) && !"net.minecraft.launchwrapper.Launch".equals(originalMainClass))
if (!LibraryAnalyzer.VANILLA_MAIN.equals(originalMainClass) && !LibraryAnalyzer.LAUNCH_WRAPPER_MAIN.equals(originalMainClass) && !LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(originalMainClass))
throw new OptiFineInstallTask.UnsupportedOptiFineInstallationException();
List<Library> libraries = new LinkedList<>();
@ -183,9 +183,9 @@ public final class OptiFineInstallTask extends Task<Version> {
setResult(new Version(
LibraryAnalyzer.LibraryType.OPTIFINE.getPatchId(),
remote.getSelfVersion(),
90000,
10000,
new Arguments().addGameArguments("--tweakClass", "optifine.OptiFineTweaker"),
"net.minecraft.launchwrapper.Launch",
LibraryAnalyzer.LAUNCH_WRAPPER_MAIN,
libraries
));

View File

@ -96,12 +96,17 @@ public class DefaultGameRepository implements GameRepository {
return versions.values();
}
@Override
public File getLibrariesDirectory(Version version) {
return new File(getBaseDirectory(), "libraries");
}
@Override
public File getLibraryFile(Version version, Library lib) {
if ("local".equals(lib.getHint()) && lib.getFileName() != null)
return new File(getVersionRoot(version.getId()), "libraries/" + lib.getFileName());
else
return new File(getBaseDirectory(), "libraries/" + lib.getPath());
return new File(getLibrariesDirectory(version), lib.getPath());
}
public Path getArtifactFile(Version version, Artifact artifact) {

View File

@ -99,6 +99,8 @@ public interface GameRepository extends VersionProvider {
*/
File getRunDirectory(String id);
File getLibrariesDirectory(Version version);
/**
* Get the library file in disk.
* This method allows versions and libraries that are not loaded by this game repository.

View File

@ -20,8 +20,7 @@ package org.jackhuang.hmcl.game;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.platform.CommandBuilder;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
/**
*
@ -31,12 +30,17 @@ public final class VersionLibraryBuilder {
private final Version version;
private final List<String> mcArgs;
private final List<Argument> game;
private final List<Argument> jvm;
private final List<Library> libraries;
private final boolean useMcArgs;
private boolean jvmChanged = false;
public VersionLibraryBuilder(Version version) {
this.version = version;
this.libraries = new ArrayList<>(version.getLibraries());
this.mcArgs = version.getMinecraftArguments().map(StringUtils::tokenize).map(ArrayList::new).orElse(null);
this.game = version.getArguments().map(Arguments::getGame).map(ArrayList::new).orElseGet(ArrayList::new);
this.jvm = new ArrayList<>(version.getArguments().map(Arguments::getJvm).orElse(Arguments.DEFAULT_JVM_ARGUMENTS));
this.useMcArgs = mcArgs != null;
}
@ -47,7 +51,10 @@ public final class VersionLibraryBuilder {
// so we regenerate the minecraftArgument without escaping.
ret = ret.setMinecraftArguments(new CommandBuilder().addAllWithoutParsing(mcArgs).toString());
}
return ret.setArguments(ret.getArguments().map(args -> args.withGame(game)).orElse(new Arguments(game, null)));
return ret.setArguments(ret.getArguments()
.map(args -> args.withGame(game))
.map(args -> jvmChanged ? args.withJvm(jvm) : args).orElse(new Arguments(game, jvmChanged ? jvm : null)))
.setLibraries(libraries);
}
public void removeTweakClass(String target) {
@ -138,8 +145,18 @@ public final class VersionLibraryBuilder {
}
}
public void addArgument(String... args) {
public void addGameArgument(String... args) {
for (String arg : args)
game.add(new StringArgument(arg));
}
public void addJvmArgument(String... args) {
jvmChanged = true;
for (String arg : args)
jvm.add(new StringArgument(arg));
}
public void addLibrary(Library library) {
libraries.add(library);
}
}

View File

@ -153,6 +153,7 @@ public class DefaultLauncher extends Launcher {
configuration.put("${natives_directory}", nativeFolder.getAbsolutePath());
configuration.put("${game_assets}", gameAssets.getAbsolutePath());
configuration.put("${assets_root}", gameAssets.getAbsolutePath());
configuration.put("${libraries_directory}", repository.getLibrariesDirectory(version).getAbsolutePath());
res.addAll(Arguments.parseArguments(version.getArguments().map(Arguments::getJvm).orElseGet(this::getDefaultJVMArguments), configuration));
if (authInfo.getArguments() != null && authInfo.getArguments().getJvm() != null && !authInfo.getArguments().getJvm().isEmpty())

View File

@ -0,0 +1 @@
org.jackhuang.hmcl.HMCLTransformerDiscoveryService

View File

@ -0,0 +1,12 @@
version '1.0'
dependencies {
compileOnly project.files("lib/modlauncher-4.1.0.jar")
}
jar {
manifest {
attributes 'Created-By': 'Copyright(c) 2013-2020 huangyuhui.',
'Implementation-Version': version
}
}

View File

@ -0,0 +1,44 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl;
import cpw.mods.modlauncher.serviceapi.ITransformerDiscoveryService;
import java.io.File;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class HMCLTransformerDiscoveryService implements ITransformerDiscoveryService {
private static final String CANDIDATES = System.getProperty("hmcl.transformer.candidates");
@Override
public List<Path> candidates(Path gameDirectory) {
return Arrays.stream(CANDIDATES.split(File.pathSeparator))
.flatMap(path -> {
try {
return Stream.of(Paths.get(path));
} catch (InvalidPathException e) {
return Stream.of();
}
}).collect(Collectors.toList());
}
}

View File

@ -0,0 +1 @@
org.jackhuang.hmcl.HMCLTransformerDiscoveryService

View File

@ -4,7 +4,6 @@ buildscript {
maven { url 'https://plugins.gradle.org/m2/' }
}
dependencies {
classpath 'org.tukaani:xz:1.8'
classpath 'gradle.plugin.com.hierynomus.gradle.plugins:license-gradle-plugin:0.15.0'
}
}

View File

@ -1,4 +1,5 @@
rootProject.name = 'HMCL3'
include 'HMCL'
include 'HMCLCore'
include 'HMCLTransformerDiscoveryService'