マインクラフト1.12以前向けのForgeGradleでrunClientをjdk8最新版で実行するには

go to [TL;DR](#tldr)

[English](https://gist.github.com/anatawa12/d61520430a96ba5939cd5a66dbba5bb3)

jdk 8u242 ~~から~~ にて、runClientタスクと`GradleStart` classを使用した実行が以下のエラーで成功しなくなりました。
> 8u252にてこのエラーの原因となる変更が差し戻されたそうなので、現在は関係ありません。

```
[18:48:19] [main/ERROR] [LaunchWrapper]: Unable to launch
java.lang.reflect.InvocationTargetException: null
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_242]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_242]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_242]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_242]
	at net.minecraft.launchwrapper.Launch.launch(Launch.java:135) [launchwrapper-1.12.jar:?]
	at net.minecraft.launchwrapper.Launch.main(Launch.java:28) [launchwrapper-1.12.jar:?]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_242]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_242]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_242]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_242]
	at net.minecraftforge.gradle.GradleStartCommon.launch(GradleStartCommon.java:97) [start/:?]
	at GradleStart.main(GradleStart.java:25) [start/:?]
Caused by: java.lang.ExceptionInInitializerError
	at org.lwjgl.MacOSXSysImplementation.<clinit>(MacOSXSysImplementation.java:51) ~[lwjgl-2.9.2-nightly-20140822.jar:?]
	at org.lwjgl.Sys.createImplementation(Sys.java:130) ~[lwjgl-2.9.2-nightly-20140822.jar:?]
Exception in thread "main" 	at org.lwjgl.Sys.<clinit>(Sys.java:111) ~[lwjgl-2.9.2-nightly-20140822.jar:?]
	at net.minecraft.client.Minecraft.getSystemTime(Minecraft.java:3159) ~[Minecraft.class:?]
	at net.minecraft.client.main.Main.main(Main.java:42) ~[Main.class:?]
	... 12 more
Caused by: java.lang.NullPointerException
	at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1847) ~[?:1.8.0_242]
	at java.lang.Runtime.loadLibrary0(Runtime.java:871) ~[?:1.8.0_242]
	at java.lang.System.loadLibrary(System.java:1124) ~[?:1.8.0_242]
	at java.awt.Toolkit$3.run(Toolkit.java:1636) ~[?:1.8.0_242]
	at java.awt.Toolkit$3.run(Toolkit.java:1634) ~[?:1.8.0_242]
	at java.security.AccessController.doPrivileged(Native Method) ~[?:1.8.0_242]
	at java.awt.Toolkit.loadLibraries(Toolkit.java:1633) ~[?:1.8.0_242]
	at java.awt.Toolkit.<clinit>(Toolkit.java:1670) ~[?:1.8.0_242]
	at org.lwjgl.MacOSXSysImplementation.<clinit>(MacOSXSysImplementation.java:51) ~[lwjgl-2.9.2-nightly-20140822.jar:?]
	at org.lwjgl.Sys.createImplementation(Sys.java:130) ~[lwjgl-2.9.2-nightly-20140822.jar:?]
	at org.lwjgl.Sys.<clinit>(Sys.java:111) ~[lwjgl-2.9.2-nightly-20140822.jar:?]
	at net.minecraft.client.Minecraft.getSystemTime(Minecraft.java:3159) ~[Minecraft.class:?]
	at net.minecraft.client.main.Main.main(Main.java:42) ~[Main.class:?]
	... 12 more
[18:48:19] [main/INFO] [STDERR]: [java.lang.ThreadGroup:uncaughtException:1052]: java.lang.reflect.InvocationTargetException
[18:48:19] [main/INFO] [STDERR]: [java.lang.ThreadGroup:uncaughtException:1052]: 	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[18:48:19] [main/INFO] [STDERR]: [java.lang.ThreadGroup:uncaughtException:1052]: 	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
[18:48:19] [main/INFO] [STDERR]: [java.lang.ThreadGroup:uncaughtException:1052]: 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[18:48:19] [main/INFO] [STDERR]: [java.lang.ThreadGroup:uncaughtException:1052]: 	at java.lang.reflect.Method.invoke(Method.java:498)
[18:48:19] [main/INFO] [STDERR]: [java.lang.ThreadGroup:uncaughtException:1052]: 	at net.minecraftforge.gradle.GradleStartCommon.launch(GradleStartCommon.java:97)
[18:48:19] [main/INFO] [STDERR]: [java.lang.ThreadGroup:uncaughtException:1052]: 	at GradleStart.main(GradleStart.java:25)
[18:48:19] [main/INFO] [STDERR]: [java.lang.ThreadGroup:uncaughtException:1061]: Caused by: net.minecraftforge.fml.relauncher.FMLSecurityManager$ExitTrappedException
[18:48:19] [main/INFO] [STDERR]: [java.lang.ThreadGroup:uncaughtException:1061]: 	at net.minecraftforge.fml.relauncher.FMLSecurityManager.checkPermission(FMLSecurityManager.java:49)
[18:48:19] [main/INFO] [STDERR]: [java.lang.ThreadGroup:uncaughtException:1061]: 	at java.lang.SecurityManager.checkExit(SecurityManager.java:761)
[18:48:19] [main/INFO] [STDERR]: [java.lang.ThreadGroup:uncaughtException:1061]: 	at java.lang.Runtime.exit(Runtime.java:108)
[18:48:19] [main/INFO] [STDERR]: [java.lang.ThreadGroup:uncaughtException:1061]: 	at java.lang.System.exit(System.java:973)
[18:48:19] [main/INFO] [STDERR]: [java.lang.ThreadGroup:uncaughtException:1061]: 	at net.minecraft.launchwrapper.Launch.launch(Launch.java:138)
[18:48:19] [main/INFO] [STDERR]: [java.lang.ThreadGroup:uncaughtException:1061]: 	at net.minecraft.launchwrapper.Launch.main(Launch.java:28)
[18:48:19] [main/INFO] [STDERR]: [java.lang.ThreadGroup:uncaughtException:1061]: 	... 6 more
```

なぜ起きるか、どのようにして回避すれば良いかを示します。

- [なぜこのエラーが起きるのか](#なぜこのエラーが起きるのか)
- [どのように回避すればよいか (ForgeGradleが更新できる場合)](#どのように回避すればよいか-forgegradleが更新できる場合)
- [どのように回避すればよいか (ForgeGradleをフォークする場合)](#どのように回避すればよいか-forgegradleをフォークする場合)
- [どのように回避すればよいか](#どのように回避すればよいか)
- [TL;DR](#tldr)

# なぜこのエラーが起きるのか

以下に示す`GradleStart`クラス内のコードが原因となっています。

```java
// some lines in hackNatives() in GradleStart
final Field sysPathsField = ClassLoader.class.getDeclaredField("sys_paths");
sysPathsField.setAccessible(true);
sysPathsField.set(null, null);
```
[FG_1.2 GradleStart.java#L82-L84](https://github.com/MinecraftForge/ForgeGradle/blob/6084129e4f407eca9fee6826aaa3276fe796553e/src/main/resources/GradleStart.java#L82-L84)

`ClassLoader.usr_path`のリセットのために、このコードは`ClassLoader.sys_paths`に`null`を代入しています.
このコードの前に`hackNatives`では`"java.library.path"` system propertyを再代入しています。. 
しかし、ClassLoaderはライブラリのロードパスのキャッシュをJVM起動時の`"java.library.path"`を使用して作成します。
そのためそのキャッシュを破棄させる必要があります。

jdk8u242までは、`ClassLoader.loadLibrary`は`ClassLoader.sys_paths`が`null`かどうかを確認して、 
`ClassLoader.sys_paths`と`ClassLoader.usr_path`を初期化していました。
`GradleStart.hackNatives`はこの動作をもとに書かれていました。

しかし、jdk8u242から`ClassLoader.loadLibrary`が`ClassLoader.sys_paths`を確認しなくなり、 
`ClassLoader.sys_paths`と`ClassLoader.usr_path`をJVM起動時に初期化するようになりました。
そのため`ClassLoader.loadLibrary`が`ClassLoader.sys_paths`が`null`のときにヌルポを投げるようになりました。
[これ](http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/1d666f78532a/)がOpenJDKの変更のdiffです。

# どのように回避すればよいか (ForgeGradleが更新できる場合)

もし1.12.2のmoddingをしている場合、MinecraftForge `1.12.2-14.23.5.2855`の開発環境を使用すると問題が発生しなくなるので、そちらを使用してください。

# どのように回避すればよいか (ForgeGradleをフォークする場合)

`usr_paths = initializePath("java.library.path")`のようなコードをリフレクションを使用して実行する必要があります。
minecraft 1.15向けのMinecraft Forgeではこの問題が修正されているので、コレと同じように書くべきだと私は考えます。 
なので以下のForgeの`ClassLoader.usr_path`をリセットするコードを使うのがいいと思います.

```java
final Method initializePathMethod = ClassLoader.class.getDeclaredMethod("initializePath", String.class);
initializePathMethod.setAccessible(true);
final Object usrPathsValue = initializePathMethod.invoke(null, "java.library.path");
final Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths");
usrPathsField.setAccessible(true);
usrPathsField.set(null, usrPathsValue);
```
[MinecraftForge userdev LaunchTesting.java#L126-L131](https://github.com/MinecraftForge/MinecraftForge/blob/9d8817b5db53db7f92ac1346b1a64523dc36dfbe/src/userdev/java/net/minecraftforge/userdev/LaunchTesting.java#L126-L131)

# どのように回避すればよいか

<!-- 2.3のFG forkはmaintenance終了予定 -->

<!-- ForgeGradleのフォークを1.2と2.3(minecraft 1.7.xと1.8.x, 1.12.x向けのForgeGradle)の２つのバージョン向けに作りました。
もしマインクラフト1.7.xと, 1.8.x, 1.12.x向けのmodderならこの私のForgeGradleのフォークを使用できます。 -->

ForgeGradleのフォークを1.2(minecraft 1.7.xと1.8.x向けのForgeGradle)の２つのバージョン向けに作りました。
もしマインクラフト1.7.xと1.8.x向けのmodderならこの私のForgeGradleのフォークを使用できます。

ForgeGradle 1.2(minecraft 1.7.xと 1.8.x)のフォークは[ここです][FG1.2]

<!-- ForgeGradle 2.3(minecraft 1.12.x)のフォークは[ここです][FG2.3] -->

もし私のフォークが使いたくないか、他のバージョンのForgeGradleを使用する場合、自分自身でForgeGradleのフォークを作ってください。

> 2021/04/24編集
> ForgeGradle 2.3のフォークは1.12.2開発に必要ではなくなったため、maintenanceを辞める予定しました。
> そのためForgeGradle 2.3への本文内のリンクを削除しました。
> 1.12.2開発をする場合は[どのように回避すればよいか (ForgeGradleが更新できる場合)](#どのように回避すればよいか-forgegradleが更新できる場合)に書かれているようにForgeGradle 4.1.xを使用してください
> もともと合ったForgeGradle 2.3(minecraft 1.12.x)のフォークは[こちら][FG2.3]になります

# TL;DR

- もし私の(anatawa12の)ForgeGradleのフォークを使用したくない場合、ForgeGradleのフォークを作成してください。
- もしForgeGradle 1.2(minecraft 1.7.xと1.8.x)を使用している場合、[私のForgeGradleのフォーク][FG1.2]を使用できます。
- もしForgeGradle 2.3(minecraft 1.12.x)を使用している場合、[私のForgeGradleのフォーク][FG2.3]を使用できます。
- 他の場合。ForgeGradleのフォークを作成してください。

[FG1.2]:https://github.com/anatawa12/ForgeGradle-1.2
[FG2.3]:https://github.com/anatawa12/ForgeGradle-2.3