Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save anatawa12/c97711ebc2eb52d87edb9c57f84756e8 to your computer and use it in GitHub Desktop.
Save anatawa12/c97711ebc2eb52d87edb9c57f84756e8 to your computer and use it in GitHub Desktop.

方法

AppClassLoaderにApache Commonsのライブラリを追加します。

実際は以下のように、リフレクションで追加します。 CoreModで使用するのであればIFMLLoadingPluginの実装クラスのコンストラクタ内で実行するのが、 通常のmodであれば@Modのクラスのコンストラクタ内で実行するのがいいかと思われます。

val addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
addURL.setAccessible(true);
addURL.invoke(Launch.classLoader.getClass().getClassLoader(), <URL to Library>);

また、にはmavenレポジトリ内のURLを指定できますが、modのjar内に含めて、

ClassInYourMod.class.getClassLoader().getResource("/path/to/library.jar")

とするのがいいかと思います。(オフラインでの起動の対応のためです。)

jarをmodのjar内に保存する方法

  1. configurationsにconfigurationを追加し、compileにextendさせます

    configurations{
        jarInJar
        compile.extendsFrom jarInJar
    }
    
  2. dependenciesでapacheのライブラリをjarInJarに追加します

    dependencies {
         // compile "org.apache.commons:commons-compress:1.9" // 削除
         jarInJar "org.apache.commons:commons-compress:1.9"
     }
    
  3. jarタスクで追加します

    jar {
         configurations.jarInJar.each {dep ->
             from(dep){
                 into "path/to" // 上の "/path/to/library.jar"のpath/toの部分。library.jarは"commons-compress-1.9.jar"のようになる。
             }
         }
     }
    

なぜロードできないか、及び上記の方法でうまくいく理由

ForgeにおけるClassLoaderの関係

ForgeにおけるClassLoaderは以下のようになっています。

// javaパッケージなどのローダ
      Bootstrap ClassLoader
      A |               A |
      | |         Parent| |Child
      | |               | V
      | |         ExtClassLoader (これはOracle/OpenJDK固有, jdkパッケージの一部がここからロード)
      | |               A |
Parent| |Child    Parent| |Child
      | |               | V
      | |          AppClassLoader // ここから↑はJDK内
      | |            A
      | |            | 必要なときに呼び出し
      | V            |
      LaunchClassLoader  // forge

LaunchClassLoaderは殆どのマインクラフトのクラスをロードするClassLoaderになります。 Classファイルの実行時編集もLaunchClassLoaderによって発生します。

なぜロードできないか

一部のパッケージ(javaパッケージ)はLaunchClassLoaderのクラスパス内には存在しません。 そのためjavaパッケージなど一部のクラスのロードには上図のようにAppClassLoaderを呼び出しています。

この一部のクラスの中に"org.apache.commons."パッケージ内のクラスが含まれています。 マインクラフト自体が使っている前提ライブラリの一部がApache Commonsのためです。 そのためmodのjar内に"org.apache.commons."パッケージがあってもロードできません。

上記の方法でうまくいく理由

Java8までは(マインクラフトはjava8で動いてます)AppClassLoaderはURLClassLoaderなので、 URLClassLoader.addURL(URL) でクラスパスに追加したいです。 しかしaddURLprotectedのため、リフレクションで呼び出します。

また、AppClassLoaderはLaunchClassLoaderをロードするClassLoaderなので、

Launch.classLoader.getClass().getClassLoader()

で取得します。よって最初のコードで追加できます。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment