├── .gitignore ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── libs ├── forge-1.14.3-27.0.50-launcher.jar ├── forge-1.7.10-10.13.4.1558-1.7.10-universal.jar └── forge-1.8-11.14.4.1563-universal.jar ├── settings.gradle └── src └── main ├── java └── io │ └── github │ └── zekerzhayard │ └── modadder │ ├── launchwrapper │ ├── ModAdder.java │ ├── v_1_7_10 │ │ └── NeteaseCoreModManager.java │ └── v_1_8_plus │ │ └── NeteaseCoreModManager.java │ └── modlauncher │ ├── ModAdder.java │ ├── ModsFolderZipLocator.java │ └── package-info.java └── resources └── META-INF ├── mods.toml └── services ├── cpw.mods.modlauncher.api.ITransformationService └── net.minecraftforge.fml.loading.moddiscovery.IModLocator /.gitignore: -------------------------------------------------------------------------------- 1 | # eclipse 2 | bin 3 | *.launch 4 | .settings 5 | .metadata 6 | .classpath 7 | .project 8 | 9 | # idea 10 | out 11 | *.ipr 12 | *.iws 13 | *.iml 14 | .idea 15 | 16 | # gradle 17 | build 18 | .gradle 19 | 20 | # other 21 | eclipse 22 | run 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NeteaseModAdder 2 | 3 | 一个用于在网易 Minecraft 中加载第三方模组的模组,以达到永久装模组的目的。 4 | 5 | ## 使用方法 6 | 0. **准备工作** 7 | - 显示文件扩展名:为了方便之后的描述和操作,需要打开显示文件扩展名 8 | * Windows 7: https://jingyan.baidu.com/article/9080802281e294fd91c80fe4.html 9 | * Windows 10:https://jingyan.baidu.com/article/f00622282564bdfbd3f0c827.html 10 | - 下载 NeteaseModAdder: https://github.com/ZekerZhayard/NeteaseModAdder/releases 11 | - 下载自己想安装的第三方模组 12 | 1. **找到 MCLDownload 文件夹**:网易 Minecraft 本体位于 `MCLDownload` 文件夹内 13 | - 打开注册表编辑器:https://jingyan.baidu.com/article/6181c3e0b33976152ef153c7.html 14 | - 在注册表编辑器内找到路径 `HKEY_CURRENT_USER\Software\Netease\MCLauncher` 15 | - 名称为 `DownloadPath` 对应的数据就是 `MCLDownload` 文件夹的路径 16 | 2. **修改 jre 版本**:目前不少模组都需要 jre8 ,由于网易 Minecraft 启动部分游戏使用的是 jre7 ,所以需要将网易 Minecraft 自带的 jre7 修改为 jre8 17 | - 查看自己的操作系统是32位的还是64位的:https://jingyan.baidu.com/article/27fa73265ed13046f8271f19.html 18 | - 如果是32位操作系统: 19 | * 下载 https://x19.gdl.netease.com/jre-v32.7z 20 | * 找到路径 `MCLDownload\ext\jre-v32` 21 | - 如果是64位操作系统: 22 | * 下载 https://x19.gdl.netease.com/jre-v64-170307.7z 23 | * 找到路径 `MCLDownload\ext\jre-v64-170307` 24 | - 如果找不到上述路径,那么需要先启动一次网易 Minecraft ,之后会自动生成 25 | - 将上述路径内的 `jre7` 和 `jre8` 两个文件夹删除 26 | - 把刚刚下载到的文件用压缩软件打开,并将其中的 `jre8` 文件夹解压到上述路径下 27 | - 在上述路径下新建一个文件夹,并命名为 `jre7` 28 | - 把 `jre8` 文件夹内的所有文件复制到 `jre7` 文件夹中就行了 29 | 3. **重命名本模组**:想了解为什么要按照下面的方法操作,请参考「模组原理」部分 30 | - 删除 `MCLDownload\cache\component` 文件夹 31 | - 在启动器组件中心里随便下载一个组件(比如「Wisdom光影 3.2」) 32 | - 下载完成后, `MCLDownload\cache\component` 又会生成,并在其中有一个数字文件夹(比如「`76002695265125376@4@18`」) 33 | - 把 NeteaseModAdder 模组文件名重命名为这个数字文件夹的名称(比如「`76002695265125376@4@18.jar`」) 34 | 4. **找到网络游戏对应文件夹**:如果你不玩网络游戏,仅玩单人或多人联机或租赁服,那么这一步可以跳过 35 | - 找到路径 `MCLDownload\Game\<你的邮箱>\NetGame\<你想加模组的网络游戏名称>` (比如「`MCLDownload\Game\abcdefg@163.com\NetGame\Hypixel`」) 36 | - 用记事本或其他代码编辑器打开其中的`config`文件(比如「`hypixel.config`」) 37 | - 找到其中 `GUID` 这一项,记录后面那一串数字(比如对于 Hypixel 就应当是 `"GUID": "76826543757722624"`) 38 | - 找到路径 `MCLDownload\cache\game` ,里面应当存在一个以上一步记录的数字为名称的文件夹,如果不存在,需要启动一次对应的网络游戏,之后会自动生成 39 | 5. **复制并修改第三方模组扩展名** 40 | - 如果你玩的是单人或多人联机或租赁服: 41 | * 找到路径 `MCLDownload\cache\game\<版本>` (比如「`MCLDownload\cache\game\V_1_12_2`」,如果没有对应版本的文件夹,需要启动一次这个版本的游戏,之后会自动生成) 42 | * 在其中新建一个文件夹并命名为 `mods` (如果没有`mods`文件夹的话) 43 | - 如果你玩的是网络游戏: 44 | * 找到路径 `MCLDownload\cache\game\\.minecraft\mods` (如果没有`mods`文件夹就新建一个) 45 | - 把 NeteaseModAdder 模组(即`76002695265125376@4@18.jar`)和你想装的第三方模组复制到`mods`文件夹中去 46 | - 把其他第三方模组的文件扩展名由`jar`全部改为`zip`即可 47 | 6. **启动游戏** 48 | 49 | 50 | ## 模组原理 51 | 本模组利用了网易 Minecraft 启动器两个缺陷。在启动器启动 Minecraft 前,清空 `MCLDownload\Game\.minecraft\mods` 文件夹后,启动器会把 `MCLDownload\cache\game\\.minecraft\mods` 或 `MCLDownload\cache\game\V_1_*_*\mods` 中的所有文件复制到 `MCLDownload\Game\.minecraft\mods` 文件夹内,随后**只**删除不符合验证且扩展名为 `jar` 的文件。 52 | - 由于「只删除`jar`文件」,因此把模组扩展名改成`zip`即可。由于网易forge1.7.10\~1.11.2支持加载以`zip`为扩展名的普通模组,网易forge1.12.2支持加载以`zip`为扩展名的普通模组和核心模组,所以本模组的作用是代替forge加载扩展名为`zip`的核心模组或1.13.2\~1.14.3的模组。 53 | - 对于`jar`文件的验证的具体过程不是非常清楚,只知道以下内容:每个组件中心的组件都有唯一的ID,这个ID就是在 `MCLDownload\cache\component` 文件夹内看到的那些,如果有检测到模组名称与当前登录的账号购买过组件中心的组件的ID匹配,那么就不会被删除。 54 | 55 | 之所以要删除 `MCLDownload\cache\component` 文件夹,是因为下载的组件我认为刚刚下载的组件混在文件夹中很难找到,那么删除之后再下载,文件夹中仅有刚刚下载到的唯一一个组件,就免去了寻找的麻烦,当然有其他方法找到也是可以的(比如根据修改时间之类的)。 -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | 2 | apply plugin: "base" 3 | apply plugin: "idea" 4 | apply plugin: "java" 5 | 6 | version = "3.1" 7 | group = "io.github.zekerzhayard" 8 | archivesBaseName = "NeteaseModAdder" 9 | 10 | repositories { 11 | jcenter() 12 | maven { 13 | url = "https://files.minecraftforge.net/maven" 14 | } 15 | maven { 16 | url = "https://libraries.minecraft.net" 17 | } 18 | } 19 | 20 | dependencies { 21 | compile fileTree(dir: 'libs', include: ['*.jar']) 22 | compile "com.google.guava:guava:15.0" 23 | compile "commons-io:commons-io:2.4" 24 | compile "cpw.mods:modlauncher:2.1.5" 25 | compile "net.minecraft:launchwrapper:1.12" 26 | compile "org.apache.commons:commons-lang3:3.1" 27 | } 28 | 29 | jar { 30 | manifest.attributes( 31 | 'FMLCorePlugin': 'io.github.zekerzhayard.modadder.launchwrapper.ModAdder', 32 | 'Name': 'NeteaseModAdder', 33 | 'Specification-Title': 'NeteaseModAdder', 34 | 'Specification-Version': project.version, 35 | 'Specification-Vendor': 'ZekerZhayard', 36 | 'Implementation-Title': 'NeteaseModAdder', 37 | 'Implementation-Version': project.version, 38 | 'Implementation-Vendor': 'ZekerZhayard' 39 | ) 40 | 41 | from { 42 | configurations.compile.filter { 43 | it.toString().contains("universal") 44 | }.collect { 45 | zipTree(it) 46 | } 47 | 48 | } 49 | include "cpw/mods/fml/relauncher/IFMLLoadingPlugin.class" 50 | include "net/minecraftforge/fml/relauncher/IFMLLoadingPlugin.class" 51 | include "io/github/zekerzhayard/**" 52 | include "META-INF/mods.toml" 53 | include "META-INF/services/cpw.mods.modlauncher.api.ITransformationService" 54 | include "META-INF/services/net.minecraftforge.fml.loading.moddiscovery.IModLocator" 55 | } 56 | 57 | sourceCompatibility = targetCompatibility = "1.7" 58 | compileJava { 59 | sourceCompatibility = targetCompatibility = "1.7" 60 | } 61 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZekerZhayard/NeteaseModAdder/4251a503012d31ce4a4e3a1f50828dc0c9ae4979/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Oct 25 18:01:41 CST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /libs/forge-1.14.3-27.0.50-launcher.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZekerZhayard/NeteaseModAdder/4251a503012d31ce4a4e3a1f50828dc0c9ae4979/libs/forge-1.14.3-27.0.50-launcher.jar -------------------------------------------------------------------------------- /libs/forge-1.7.10-10.13.4.1558-1.7.10-universal.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZekerZhayard/NeteaseModAdder/4251a503012d31ce4a4e3a1f50828dc0c9ae4979/libs/forge-1.7.10-10.13.4.1558-1.7.10-universal.jar -------------------------------------------------------------------------------- /libs/forge-1.8-11.14.4.1563-universal.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZekerZhayard/NeteaseModAdder/4251a503012d31ce4a4e3a1f50828dc0c9ae4979/libs/forge-1.8-11.14.4.1563-universal.jar -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = "NeteaseModAdder" -------------------------------------------------------------------------------- /src/main/java/io/github/zekerzhayard/modadder/launchwrapper/ModAdder.java: -------------------------------------------------------------------------------- 1 | package io.github.zekerzhayard.modadder.launchwrapper; 2 | 3 | import java.io.File; 4 | import java.util.Map; 5 | 6 | import cpw.mods.fml.relauncher.IFMLLoadingPlugin; 7 | import io.github.zekerzhayard.modadder.launchwrapper.v_1_7_10.NeteaseCoreModManager; 8 | import net.minecraft.launchwrapper.Launch; 9 | import org.apache.commons.lang3.reflect.FieldUtils; 10 | 11 | public class ModAdder implements IFMLLoadingPlugin, net.minecraftforge.fml.relauncher.IFMLLoadingPlugin { 12 | static { 13 | try { 14 | String mcVersion = (String) FieldUtils.readStaticField(Launch.class.getClassLoader().loadClass("net.minecraftforge.common.MinecraftForge"), "MC_VERSION"); 15 | if (mcVersion.equals("1.7.10")) { 16 | NeteaseCoreModManager.discoverCoreMods(new File("."), Launch.classLoader); 17 | } else if (!mcVersion.equals("1.12.2")) { 18 | io.github.zekerzhayard.modadder.launchwrapper.v_1_8_plus.NeteaseCoreModManager.discoverCoreMods(new File("."), Launch.classLoader); 19 | } 20 | } catch (Exception e) { 21 | e.printStackTrace(); 22 | } 23 | } 24 | 25 | @Override 26 | public String[] getASMTransformerClass() { 27 | return null; 28 | } 29 | 30 | @Override 31 | public String getModContainerClass() { 32 | return null; 33 | } 34 | 35 | @Override 36 | public String getSetupClass() { 37 | return null; 38 | } 39 | 40 | @Override 41 | public void injectData(Map data) { 42 | 43 | } 44 | 45 | @Override 46 | public String getAccessTransformerClass() { 47 | return null; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/io/github/zekerzhayard/modadder/launchwrapper/v_1_7_10/NeteaseCoreModManager.java: -------------------------------------------------------------------------------- 1 | package io.github.zekerzhayard.modadder.launchwrapper.v_1_7_10; 2 | 3 | import java.io.File; 4 | import java.io.FilenameFilter; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.lang.reflect.Field; 8 | import java.lang.reflect.InvocationTargetException; 9 | import java.lang.reflect.Method; 10 | import java.net.MalformedURLException; 11 | import java.util.Arrays; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.jar.Attributes; 15 | import java.util.jar.JarEntry; 16 | import java.util.jar.JarFile; 17 | import java.util.jar.Manifest; 18 | 19 | import com.google.common.base.Charsets; 20 | import com.google.common.base.Strings; 21 | import com.google.common.collect.ImmutableList; 22 | import com.google.common.collect.ObjectArrays; 23 | import com.google.common.io.ByteSource; 24 | import com.google.common.primitives.Ints; 25 | import cpw.mods.fml.common.asm.transformers.ModAccessTransformer; 26 | import cpw.mods.fml.relauncher.CoreModManager; 27 | import cpw.mods.fml.relauncher.FMLInjectionData; 28 | import cpw.mods.fml.relauncher.FMLLaunchHandler; 29 | import cpw.mods.fml.relauncher.FMLRelaunchLog; 30 | import cpw.mods.fml.relauncher.FileListHelper; 31 | import cpw.mods.fml.relauncher.ModListHelper; 32 | import cpw.mods.fml.relauncher.Side; 33 | import net.minecraft.launchwrapper.LaunchClassLoader; 34 | import org.apache.logging.log4j.Level; 35 | 36 | @SuppressWarnings("unchecked") 37 | public class NeteaseCoreModManager { 38 | private static final Attributes.Name COREMODCONTAINSFMLMOD = (Attributes.Name) NeteaseCoreModManager.reflectField(CoreModManager.class, "COREMODCONTAINSFMLMOD"); 39 | private static final Attributes.Name MODTYPE = (Attributes.Name) NeteaseCoreModManager.reflectField(CoreModManager.class, "MODTYPE"); 40 | private static final Attributes.Name MODSIDE = (Attributes.Name) NeteaseCoreModManager.reflectField(CoreModManager.class, "MODSIDE"); 41 | 42 | public static List loadedCoremods = (List) NeteaseCoreModManager.reflectField(CoreModManager.class, "loadedCoremods"); 43 | public static List reparsedCoremods = (List) NeteaseCoreModManager.reflectField(CoreModManager.class, "reparsedCoremods"); 44 | private static Side side = (Side) NeteaseCoreModManager.reflectField(FMLLaunchHandler.class, "side"); 45 | private static String mccversion = (String) NeteaseCoreModManager.reflectField(FMLInjectionData.class, "mccversion"); 46 | 47 | private static Method methodHandleCascadingTweak = NeteaseCoreModManager.reflectMethod(CoreModManager.class, "handleCascadingTweak", File.class, JarFile.class, String.class, LaunchClassLoader.class, Integer.class); 48 | private static Method methodLoadCoreMod = NeteaseCoreModManager.reflectMethod(CoreModManager.class, "loadCoreMod", LaunchClassLoader.class, String.class, File.class); 49 | private static Method methodSetupCoreModDir = NeteaseCoreModManager.reflectMethod(CoreModManager.class, "setupCoreModDir", File.class); 50 | 51 | private static Object reflectField(Class clazz, String name) { 52 | try { 53 | Field field = clazz.getDeclaredField(name); 54 | field.setAccessible(true); 55 | return field.get(clazz); 56 | } catch (Exception e) { 57 | e.printStackTrace(); 58 | return null; 59 | } 60 | } 61 | 62 | private static Method reflectMethod(Class clazz, String name, Class... parameterTypes) { 63 | try { 64 | Method method = clazz.getDeclaredMethod(name, parameterTypes); 65 | method.setAccessible(true); 66 | return method; 67 | } catch (Exception e) { 68 | e.printStackTrace(); 69 | return null; 70 | } 71 | } 72 | 73 | private static void addJar(final JarFile jar) throws IOException { 74 | Manifest manifest = jar.getManifest(); 75 | String atList = manifest.getMainAttributes().getValue("FMLAT"); 76 | if (atList == null) { 77 | return; 78 | } 79 | for (String at : atList.split(" ")) { 80 | final JarEntry jarEntry = jar.getJarEntry("META-INF/" + at); 81 | if (jarEntry != null) { 82 | try { 83 | ((Map) NeteaseCoreModManager.reflectField(Class.forName(ModAccessTransformer.class.getName(), false, NeteaseCoreModManager.class.getClassLoader().getClass().getClassLoader()), "embedded")).put(String.format("%s!META-INF/%s", jar.getName(), at), new ByteSource() { 84 | @Override 85 | public InputStream openStream() throws IOException { 86 | return jar.getInputStream(jarEntry); 87 | } 88 | }.asCharSource(Charsets.UTF_8).read()); 89 | } catch (ClassNotFoundException e) { 90 | e.printStackTrace(); 91 | } 92 | } 93 | } 94 | } 95 | 96 | public static void discoverCoreMods(File mcDir, LaunchClassLoader classLoader) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { 97 | File coreMods = (File) NeteaseCoreModManager.methodSetupCoreModDir.invoke(null, mcDir); // File coreMods = setupCoreModDir(mcDir); 98 | FilenameFilter ff = new FilenameFilter() { 99 | @Override 100 | public boolean accept(File dir, String name) { 101 | return name.endsWith(".zip"); 102 | } 103 | }; 104 | File[] coreModList = coreMods.listFiles(ff); 105 | File versionedModDir = new File(coreMods, NeteaseCoreModManager.mccversion); // File versionedModDir = new File(coreMods, FMLInjectionData.mccversion); 106 | if (versionedModDir.isDirectory()) { 107 | File[] versionedCoreMods = versionedModDir.listFiles(ff); 108 | coreModList = ObjectArrays.concat(coreModList, versionedCoreMods, File.class); 109 | } 110 | 111 | coreModList = ObjectArrays.concat(coreModList, ModListHelper.additionalMods.values().toArray(new File[0]), File.class); 112 | 113 | coreModList = FileListHelper.sortFileList(coreModList); 114 | 115 | for (File coreMod : coreModList) { 116 | FMLRelaunchLog.fine("Examining for coremod candidacy %s", coreMod.getName()); 117 | JarFile jar = null; 118 | Attributes mfAttributes; 119 | try { 120 | jar = new JarFile(coreMod); 121 | if (jar.getManifest() == null) { 122 | // // Not a coremod and no access transformer list 123 | continue; 124 | } 125 | NeteaseCoreModManager.addJar(jar); 126 | mfAttributes = jar.getManifest().getMainAttributes(); 127 | } catch (IOException ioe) { 128 | FMLRelaunchLog.log(Level.ERROR, ioe, "Unable to read the jar file %s - ignoring", coreMod.getName()); 129 | continue; 130 | } finally { 131 | if (jar != null) { 132 | try { 133 | jar.close(); 134 | } catch (IOException e) { 135 | // // Noise 136 | } 137 | } 138 | } 139 | String cascadedTweaker = mfAttributes.getValue("TweakClass"); 140 | if (cascadedTweaker != null) { 141 | FMLRelaunchLog.info("Loading tweaker %s from %s", cascadedTweaker, coreMod.getName()); 142 | Integer sortOrder = Ints.tryParse(Strings.nullToEmpty(mfAttributes.getValue("TweakOrder"))); 143 | sortOrder = (sortOrder == null ? Integer.valueOf(0) : sortOrder); 144 | NeteaseCoreModManager.methodHandleCascadingTweak.invoke(null, coreMod, jar, cascadedTweaker, classLoader, sortOrder); // CoreModManager.handleCascadingTweak(coreMod, jar, cascadedTweaker, classLoader, sortOrder); 145 | NeteaseCoreModManager.loadedCoremods.add(coreMod.getName()); // loadedCoremods.add(coreMod.getName()); 146 | continue; 147 | } 148 | List modTypes = mfAttributes.containsKey(NeteaseCoreModManager.MODTYPE) ? Arrays.asList(mfAttributes.getValue(NeteaseCoreModManager.MODTYPE).split(",")) : ImmutableList.of("FML"); // List modTypes = mfAttributes.containsKey(MODTYPE) ? Arrays.asList(mfAttributes.getValue(MODTYPE).split(",")) : ImmutableList.of("FML"); 149 | 150 | if (!modTypes.contains("FML")) { 151 | FMLRelaunchLog.fine("Adding %s to the list of things to skip. It is not an FML mod, it has types %s", coreMod.getName(), modTypes); 152 | NeteaseCoreModManager.loadedCoremods.add(coreMod.getName()); // loadedCoremods.add(coreMod.getName()); 153 | continue; 154 | } 155 | String modSide = mfAttributes.containsKey(NeteaseCoreModManager.MODSIDE) ? mfAttributes.getValue(NeteaseCoreModManager.MODSIDE) : "BOTH"; // String modSide = mfAttributes.containsKey(MODSIDE) ? mfAttributes.getValue(MODSIDE) : "BOTH"; 156 | if (!("BOTH".equals(modSide) || NeteaseCoreModManager.side.name().equals(modSide))) { // if (!("BOTH".equals(modSide) || FMLLaunchHandler.side.name().equals(modSide))) { 157 | FMLRelaunchLog.fine("Mod %s has ModSide meta-inf value %s, and we're %s. It will be ignored", coreMod.getName(), modSide, NeteaseCoreModManager.side.name()); // FMLRelaunchLog.fine("Mod %s has ModSide meta-inf value %s, and we're %s. It will be ignored", coreMod.getName(), modSide, FMLLaunchHandler.side.name()); 158 | NeteaseCoreModManager.loadedCoremods.add(coreMod.getName()); // loadedCoremods.add(coreMod.getName()); 159 | continue; 160 | } 161 | String fmlCorePlugin = mfAttributes.getValue("FMLCorePlugin"); 162 | if (fmlCorePlugin == null) { 163 | // // Not a coremod 164 | FMLRelaunchLog.fine("Not found coremod data in %s", coreMod.getName()); 165 | continue; 166 | } 167 | // // Support things that are mod jars, but not FML mod jars 168 | try { 169 | classLoader.addURL(coreMod.toURI().toURL()); 170 | if (!mfAttributes.containsKey(NeteaseCoreModManager.COREMODCONTAINSFMLMOD)) { // if (!mfAttributes.containsKey(COREMODCONTAINSFMLMOD)) { 171 | FMLRelaunchLog.finer("Adding %s to the list of known coremods, it will not be examined again", coreMod.getName()); 172 | NeteaseCoreModManager.loadedCoremods.add(coreMod.getName()); // loadedCoremods.add(coreMod.getName()); 173 | } else { 174 | FMLRelaunchLog.finer("Found FMLCorePluginContainsFMLMod marker in %s, it will be examined later for regular @Mod instances", coreMod.getName()); 175 | NeteaseCoreModManager.reparsedCoremods.add(coreMod.getName()); // reparsedCoremods.add(coreMod.getName()); 176 | } 177 | } catch (MalformedURLException e) { 178 | FMLRelaunchLog.log(Level.ERROR, e, "Unable to convert file into a URL. weird"); 179 | continue; 180 | } 181 | NeteaseCoreModManager.methodLoadCoreMod.invoke(null, classLoader, fmlCorePlugin, coreMod); // loadCoreMod(classLoader, fmlCorePlugin, coreMod); 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/main/java/io/github/zekerzhayard/modadder/launchwrapper/v_1_8_plus/NeteaseCoreModManager.java: -------------------------------------------------------------------------------- 1 | package io.github.zekerzhayard.modadder.launchwrapper.v_1_8_plus; 2 | 3 | import java.io.File; 4 | import java.io.FilenameFilter; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.lang.reflect.Field; 8 | import java.lang.reflect.InvocationTargetException; 9 | import java.lang.reflect.Method; 10 | import java.net.MalformedURLException; 11 | import java.util.Arrays; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.jar.Attributes; 15 | import java.util.jar.JarEntry; 16 | import java.util.jar.JarFile; 17 | import java.util.jar.Manifest; 18 | 19 | import com.google.common.base.Charsets; 20 | import com.google.common.base.Strings; 21 | import com.google.common.collect.ImmutableList; 22 | import com.google.common.collect.ObjectArrays; 23 | import com.google.common.io.ByteSource; 24 | import com.google.common.primitives.Ints; 25 | import net.minecraft.launchwrapper.LaunchClassLoader; 26 | import net.minecraftforge.fml.common.asm.transformers.ModAccessTransformer; 27 | import net.minecraftforge.fml.relauncher.CoreModManager; 28 | import net.minecraftforge.fml.relauncher.FMLInjectionData; 29 | import net.minecraftforge.fml.relauncher.FMLLaunchHandler; 30 | import net.minecraftforge.fml.relauncher.FMLRelaunchLog; 31 | import net.minecraftforge.fml.relauncher.FileListHelper; 32 | import net.minecraftforge.fml.relauncher.ModListHelper; 33 | import net.minecraftforge.fml.relauncher.Side; 34 | import org.apache.logging.log4j.Level; 35 | 36 | @SuppressWarnings("unchecked") 37 | public class NeteaseCoreModManager { 38 | private static final Attributes.Name COREMODCONTAINSFMLMOD = (Attributes.Name) NeteaseCoreModManager.reflectField(CoreModManager.class, "COREMODCONTAINSFMLMOD"); 39 | private static final Attributes.Name MODTYPE = (Attributes.Name) NeteaseCoreModManager.reflectField(CoreModManager.class, "MODTYPE"); 40 | private static final Attributes.Name MODSIDE = (Attributes.Name) NeteaseCoreModManager.reflectField(CoreModManager.class, "MODSIDE"); 41 | 42 | public static List candidateModFiles; 43 | public static List ignoredModFiles; 44 | private static Side side = (Side) NeteaseCoreModManager.reflectField(FMLLaunchHandler.class, "side"); 45 | private static String mccversion; 46 | 47 | private static Method methodExtractContainedDepJars; 48 | private static Method methodHandleCascadingTweak = NeteaseCoreModManager.reflectMethod(CoreModManager.class, "handleCascadingTweak", File.class, JarFile.class, String.class, LaunchClassLoader.class, Integer.class); 49 | private static Method methodLoadCoreMod = NeteaseCoreModManager.reflectMethod(CoreModManager.class, "loadCoreMod", LaunchClassLoader.class, String.class, File.class); 50 | private static Method methodSetupCoreModDir = NeteaseCoreModManager.reflectMethod(CoreModManager.class, "setupCoreModDir", File.class); 51 | 52 | static { 53 | NeteaseCoreModManager.mccversion = (String) NeteaseCoreModManager.reflectField(FMLInjectionData.class, "mccversion"); 54 | if (NeteaseCoreModManager.mccversion.equals("1.8")) { 55 | NeteaseCoreModManager.candidateModFiles = (List) NeteaseCoreModManager.reflectField(CoreModManager.class, "reparsedCoremods"); 56 | NeteaseCoreModManager.ignoredModFiles = (List) NeteaseCoreModManager.reflectField(CoreModManager.class, "loadedCoremods"); 57 | } else { 58 | NeteaseCoreModManager.candidateModFiles = (List) NeteaseCoreModManager.reflectField(CoreModManager.class, "candidateModFiles"); 59 | NeteaseCoreModManager.ignoredModFiles = (List) NeteaseCoreModManager.reflectField(CoreModManager.class, "ignoredModFiles"); 60 | } 61 | if (NeteaseCoreModManager.mccversion.equals("1.8.9") || NeteaseCoreModManager.mccversion.equals("1.9.4")) { 62 | NeteaseCoreModManager.methodExtractContainedDepJars = NeteaseCoreModManager.reflectMethod(CoreModManager.class, "extractContainedDepJars", JarFile.class, File.class); 63 | } else if (NeteaseCoreModManager.mccversion.equals("1.10.2") || NeteaseCoreModManager.mccversion.equals("1.11.2")) { 64 | NeteaseCoreModManager.methodExtractContainedDepJars = NeteaseCoreModManager.reflectMethod(CoreModManager.class, "extractContainedDepJars", JarFile.class, File.class, File.class); 65 | } 66 | } 67 | 68 | private static Object reflectField(Class clazz, String name) { 69 | try { 70 | Field field = clazz.getDeclaredField(name); 71 | field.setAccessible(true); 72 | return field.get(clazz); 73 | } catch (Exception e) { 74 | e.printStackTrace(); 75 | return null; 76 | } 77 | } 78 | 79 | private static Method reflectMethod(Class clazz, String name, Class... parameterTypes) { 80 | try { 81 | Method method = clazz.getDeclaredMethod(name, parameterTypes); 82 | method.setAccessible(true); 83 | return method; 84 | } catch (Exception e) { 85 | e.printStackTrace(); 86 | return null; 87 | } 88 | } 89 | 90 | private static void addJar(final JarFile jar) throws IOException { 91 | Manifest manifest = jar.getManifest(); 92 | String atList = manifest.getMainAttributes().getValue("FMLAT"); 93 | if (atList == null) { 94 | return; 95 | } 96 | for (String at : atList.split(" ")) { 97 | final JarEntry jarEntry = jar.getJarEntry("META-INF/" + at); 98 | if (jarEntry != null) { 99 | try { 100 | ((Map) NeteaseCoreModManager.reflectField(Class.forName(ModAccessTransformer.class.getName(), false, NeteaseCoreModManager.class.getClassLoader().getClass().getClassLoader()), "embedded")).put(String.format("%s!META-INF/%s", jar.getName(), at), new ByteSource() { 101 | @Override 102 | public InputStream openStream() throws IOException { 103 | return jar.getInputStream(jarEntry); 104 | } 105 | }.asCharSource(Charsets.UTF_8).read()); 106 | } catch (ClassNotFoundException e) { 107 | e.printStackTrace(); 108 | } 109 | } 110 | } 111 | } 112 | 113 | public static void discoverCoreMods(File mcDir, LaunchClassLoader classLoader) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { 114 | File coreMods = (File) NeteaseCoreModManager.methodSetupCoreModDir.invoke(null, mcDir); 115 | FilenameFilter ff = new FilenameFilter() { 116 | @Override 117 | public boolean accept(File dir, String name) { 118 | return name.endsWith(".zip"); 119 | } 120 | }; 121 | File[] coreModList = coreMods.listFiles(ff); 122 | File versionedModDir = new File(coreMods, NeteaseCoreModManager.mccversion); 123 | if (versionedModDir.isDirectory()) { 124 | File[] versionedCoreMods = versionedModDir.listFiles(ff); 125 | coreModList = ObjectArrays.concat(coreModList, versionedCoreMods, File.class); 126 | } 127 | 128 | coreModList = ObjectArrays.concat(coreModList, ModListHelper.additionalMods.values().toArray(new File[0]), File.class); 129 | 130 | coreModList = FileListHelper.sortFileList(coreModList); 131 | 132 | for (File coreMod : coreModList) { 133 | FMLRelaunchLog.fine("Examining for coremod candidacy %s", coreMod.getName()); 134 | JarFile jar = null; 135 | Attributes mfAttributes; 136 | String fmlCorePlugin; 137 | try { 138 | jar = new JarFile(coreMod); 139 | if (jar.getManifest() == null) { 140 | // Not a coremod and no access transformer list 141 | continue; 142 | } 143 | NeteaseCoreModManager.addJar(jar); 144 | mfAttributes = jar.getManifest().getMainAttributes(); 145 | String cascadedTweaker = mfAttributes.getValue("TweakClass"); 146 | if (cascadedTweaker != null) { 147 | FMLRelaunchLog.info("Loading tweaker %s from %s", cascadedTweaker, coreMod.getName()); 148 | Integer sortOrder = Ints.tryParse(Strings.nullToEmpty(mfAttributes.getValue("TweakOrder"))); 149 | sortOrder = (sortOrder == null ? Integer.valueOf(0) : sortOrder); 150 | NeteaseCoreModManager.methodHandleCascadingTweak.invoke(null, coreMod, jar, cascadedTweaker, classLoader, sortOrder); 151 | NeteaseCoreModManager.ignoredModFiles.add(coreMod.getName()); 152 | continue; 153 | } 154 | List modTypes = mfAttributes.containsKey(NeteaseCoreModManager.MODTYPE) ? Arrays.asList(mfAttributes.getValue(NeteaseCoreModManager.MODTYPE).split(",")) : ImmutableList.of("FML"); 155 | 156 | if (!modTypes.contains("FML")) { 157 | FMLRelaunchLog.fine("Adding %s to the list of things to skip. It is not an FML mod, it has types %s", coreMod.getName(), modTypes); 158 | NeteaseCoreModManager.ignoredModFiles.add(coreMod.getName()); 159 | continue; 160 | } 161 | String modSide = mfAttributes.containsKey(NeteaseCoreModManager.MODSIDE) ? mfAttributes.getValue(NeteaseCoreModManager.MODSIDE) : "BOTH"; 162 | if (!("BOTH".equals(modSide) || NeteaseCoreModManager.side.name().equals(modSide))) { 163 | FMLRelaunchLog.fine("Mod %s has ModSide meta-inf value %s, and we're %s. It will be ignored", coreMod.getName(), modSide, NeteaseCoreModManager.side.name()); 164 | NeteaseCoreModManager.ignoredModFiles.add(coreMod.getName()); 165 | continue; 166 | } 167 | if (NeteaseCoreModManager.mccversion.equals("1.8.9") || NeteaseCoreModManager.mccversion.equals("1.9.4")) { 168 | ModListHelper.additionalMods.putAll((Map) NeteaseCoreModManager.methodExtractContainedDepJars.invoke(null, jar, versionedModDir)); 169 | } else if (NeteaseCoreModManager.mccversion.equals("1.10.2") || NeteaseCoreModManager.mccversion.equals("1.11.2")) { 170 | ModListHelper.additionalMods.putAll((Map) NeteaseCoreModManager.methodExtractContainedDepJars.invoke(null, jar, coreMods, versionedModDir)); 171 | } 172 | fmlCorePlugin = mfAttributes.getValue("FMLCorePlugin"); 173 | if (fmlCorePlugin == null) { 174 | // Not a coremod 175 | FMLRelaunchLog.fine("Not found coremod data in %s", coreMod.getName()); 176 | continue; 177 | } 178 | } catch (IOException ioe) { 179 | FMLRelaunchLog.log(Level.ERROR, ioe, "Unable to read the jar file %s - ignoring", coreMod.getName()); 180 | continue; 181 | } finally { 182 | if (jar != null) { 183 | try { 184 | jar.close(); 185 | } catch (IOException e) { 186 | // Noise 187 | } 188 | } 189 | } 190 | // Support things that are mod jars, but not FML mod jars 191 | try { 192 | classLoader.addURL(coreMod.toURI().toURL()); 193 | if (!mfAttributes.containsKey(NeteaseCoreModManager.COREMODCONTAINSFMLMOD)) { 194 | FMLRelaunchLog.finer("Adding %s to the list of known coremods, it will not be examined again", coreMod.getName()); 195 | NeteaseCoreModManager.ignoredModFiles.add(coreMod.getName()); 196 | } else { 197 | FMLRelaunchLog.finer("Found FMLCorePluginContainsFMLMod marker in %s, it will be examined later for regular @Mod instances", coreMod.getName()); 198 | NeteaseCoreModManager.candidateModFiles.add(coreMod.getName()); 199 | } 200 | } catch (MalformedURLException e) { 201 | FMLRelaunchLog.log(Level.ERROR, e, "Unable to convert file into a URL. weird"); 202 | continue; 203 | } 204 | NeteaseCoreModManager.methodLoadCoreMod.invoke(null, classLoader, fmlCorePlugin, coreMod); 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/main/java/io/github/zekerzhayard/modadder/modlauncher/ModAdder.java: -------------------------------------------------------------------------------- 1 | package io.github.zekerzhayard.modadder.modlauncher; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.lang.reflect.Method; 6 | import java.net.URL; 7 | import java.net.URLClassLoader; 8 | import java.nio.file.Files; 9 | import java.nio.file.Path; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.Set; 13 | import java.util.zip.ZipFile; 14 | import javax.annotation.Nonnull; 15 | 16 | import com.google.common.collect.Lists; 17 | import cpw.mods.modlauncher.api.IEnvironment; 18 | import cpw.mods.modlauncher.api.ITransformationService; 19 | import cpw.mods.modlauncher.api.ITransformer; 20 | import net.minecraftforge.fml.loading.FMLPaths; 21 | import org.apache.commons.io.FileUtils; 22 | import org.apache.logging.log4j.LogManager; 23 | 24 | public class ModAdder implements ITransformationService { 25 | static { 26 | try { 27 | Method method_addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); 28 | method_addURL.setAccessible(true); 29 | method_addURL.invoke(Thread.currentThread().getContextClassLoader(), ModAdder.class.getProtectionDomain().getCodeSource().getLocation()); 30 | 31 | List additionalPaths = candidatesModDirTransformer(new File("").getAbsoluteFile().toPath()); 32 | for (Path p : additionalPaths) { 33 | method_addURL.invoke(ModAdder.class.getClassLoader(), p.toUri().toURL()); 34 | } 35 | } catch (Exception e) { 36 | e.printStackTrace(); 37 | } 38 | } 39 | 40 | @Nonnull 41 | @Override 42 | public String name() { 43 | return "NeteaseModAdder"; 44 | } 45 | 46 | @Override 47 | public void initialize(IEnvironment environment) { 48 | 49 | } 50 | 51 | @Override 52 | public void beginScanning(IEnvironment environment) { 53 | 54 | } 55 | 56 | @Override 57 | public void onLoad(IEnvironment env, Set otherServices) { 58 | 59 | } 60 | 61 | @Nonnull 62 | @Override 63 | public List transformers() { 64 | return new ArrayList<>(); 65 | } 66 | 67 | static List candidatesModDirTransformer(final Path gameDirectory) { 68 | final Path modsDir = gameDirectory.resolve(FMLPaths.MODSDIR.relative()); 69 | List paths = new ArrayList<>(); 70 | List files = Lists.newArrayList(FileUtils.listFiles(modsDir.toFile(), new String[] {"zip"}, false)); 71 | for (File file : files) { 72 | try { 73 | Path p = file.toPath(); 74 | if (!Files.isRegularFile(p) || Files.size(p) == 0) { 75 | continue; 76 | } 77 | try (ZipFile zf = new ZipFile(new File(p.toUri()))) { 78 | if (zf.getEntry("META-INF/services/cpw.mods.modlauncher.api.ITransformationService") != null) { 79 | paths.add(p); 80 | } 81 | } catch (IOException ioe) { 82 | LogManager.getLogger().error("Zip Error when loading jar file {}", p, ioe); 83 | } 84 | } catch (IOException | IllegalStateException ioe) { 85 | LogManager.getLogger().error("Error during early discovery", ioe); 86 | } 87 | } 88 | return paths; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/io/github/zekerzhayard/modadder/modlauncher/ModsFolderZipLocator.java: -------------------------------------------------------------------------------- 1 | package io.github.zekerzhayard.modadder.modlauncher; 2 | 3 | import java.io.File; 4 | import java.nio.file.Path; 5 | import java.util.List; 6 | 7 | import com.google.common.collect.Lists; 8 | import net.minecraftforge.fml.loading.FMLPaths; 9 | import net.minecraftforge.fml.loading.moddiscovery.ModFile; 10 | import net.minecraftforge.fml.loading.moddiscovery.ModsFolderLocator; 11 | import org.apache.commons.io.FileUtils; 12 | 13 | public class ModsFolderZipLocator extends ModsFolderLocator { 14 | private Path modFolder = FMLPaths.MODSDIR.get(); 15 | 16 | @Override 17 | public List scanMods() { 18 | List modsFile = Lists.newArrayList(FileUtils.listFiles(this.modFolder.toFile(), new String[] { "zip" }, false)); 19 | List excluded = ModAdder.candidatesModDirTransformer(FMLPaths.GAMEDIR.get()); 20 | List modFiles = Lists.newArrayList(); 21 | for (File modFile : modsFile) { 22 | Path p = modFile.toPath(); 23 | if (excluded.contains(p)) { 24 | continue; 25 | } 26 | ModFile file = new ModFile(p, this); 27 | modFiles.add(file); 28 | this.modJars.put(file, this.createFileSystem(file)); 29 | } 30 | return modFiles; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/github/zekerzhayard/modadder/modlauncher/package-info.java: -------------------------------------------------------------------------------- 1 | @ParametersAreNonnullByDefault 2 | package io.github.zekerzhayard.modadder.modlauncher; 3 | 4 | import javax.annotation.ParametersAreNonnullByDefault; -------------------------------------------------------------------------------- /src/main/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader="javafml" 2 | loaderVersion="[25,)" 3 | [[mods]] 4 | modId="neteasemodadder" 5 | version="3.1" 6 | displayName="NeteaseModAdder" 7 | description=''' 8 | Netease Minecraft Mods Adder. 9 | ''' -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService: -------------------------------------------------------------------------------- 1 | io.github.zekerzhayard.modadder.modlauncher.ModAdder -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/net.minecraftforge.fml.loading.moddiscovery.IModLocator: -------------------------------------------------------------------------------- 1 | io.github.zekerzhayard.modadder.modlauncher.ModsFolderZipLocator --------------------------------------------------------------------------------