LoginSignup
4
4

More than 3 years have passed since last update.

MinecraftのForge ModをScalaで書く

Last updated at Posted at 2021-04-07

2021/04/07 時点での Minecraft 1.16.5 のMod (FML)をScalaで開発する方法をメモ程度に残します。簡単なメッセージを出す部分までを作ります。ほとんどMinecraft Wiki (Javaでの方法)に従っています。
書き忘れ・訂正などありましたらよろしくお願いします。

環境

Minecraft 1.16.5
Forge 1.16.5-36.1.0
JDK 1.8.0_281
Scala 2.13.4

sbtは使わずgradleでビルドします。

セットアップ

Minecraft Wikiのページに従ってIntelliJ IDEAで環境を構築します。追記しつつまとめますが必要なければ読み飛ばしてください。

IntelliJ IDEAのインストールが前提です。

1. MDKの準備

ForgeのMDK(Mod Development Kit)をForgeの配布サイト のShow all Versionsからダウンロードし、zipの中身の

  • gradle/
  • src/
  • build.gradle
  • gradlew
  • gradlew.bat

をプロジェクトフォルダに配置します。

build.gradleを次のように変更します。難読化を解除する際の名前のマップだと思います(詳しく調べてません)。

old
mappings channel: 'official', version: '1.16.5'
new
mappings channel: 'snapshot', version: '20210309-1.16.5'

2. IntelliJ IDEAにインポート

IntelliJ IDEAを起動し、Open File or Project から build.gradle を選択するとインポートできます。初回は時間がかかります。インポートが完了すると画面右端にGradleのタブができるので、 Tasks/fg_runs/genIntellijRuns を実行します。

1でのマップの変更に伴い src/main/ExampleMod.java を次のように変更します。

old
LOGGER.info("Got game settings {}", event.getMinecraftSupplier().get().options);
new
LOGGER.info("Got game settings {}", event.getMinecraftSupplier().get().gameSettings);

3. 名前の変更

ExampleModとなっているため名前を変更します。2021/04/07 時点では Minecraft Wiki のページは記述途中であったため書きます。

Mod名, ModIDの変更

mods.tomlを編集します。[[mods]]内のmodIdを変更します。その他modに関する情報は[[mods]]にあるので適宜変更・追加してください。以下に例を示します。

折りたたみ
mods.toml
modLoader="javafml"
loaderVersion="[36,)"
license="MIT"

[[mods]]
    modId="MOD_NAME"
    namespace="MOD_NAME"
    version="0.1"
    displayName="MOD_NAME"
    authors="Tsukina_7mochi"
    description='''
Created

with

Scala!
    '''

[[dependencies.MOD_NAME]]
    modId="forge"
    mandatory=true
    versionRange="[36,)"
    ordering="NONE"
    side="BOTH"

[[dependencies.MOD_NAME]]
    modId="minecraft"
    mandatory=true
    versionRange="[1.16.5,1.17)"
    ordering="NONE"
    side="BOTH"

次に ExampleMod.java を編集します。

@Modアノテーションの引数の部分を mods.toml と同じにします。またクラス名を変更します。おすすめは次のような感じです(IronChestsのソースを見てきた)。

ModName.java
@Mod(Chalcedony.MODID)
class Chalcedony{
    public static final String MODID =  "MOD ID";

    // ... 処理

}
パッケージ名の変更

ご自由にどうぞ
Scalaではパッケージ名とディレクトリ構成は一致していなくても問題ないですがJava同様一致させるのが慣例らしいです。

Scalaを使えるようにする

そのままだとScalaでコーディングができないため設定をします。全体をScalaで書くとFMLから参照できない1ため、FMLから呼ばれる部分のみJavaで記述します2

IntelliJ IDEAにScalaプラグインを追加

File > Settings > Plugins からScalaプラグインを検索して導入します。導入後エディタの再起動が必要です。

build.gradleの編集

ここが2番目に躓きました(1番は全部Scalaで書けない理由を探すところ)

Scala Pluginの追加

ルートに次の記述を追加します。最初の方に net.minecraftforge.gradle とか maven-publish をapplyしてるところがあるのでそこに追記すればいいと思います。

build.gralde
apply plugin: 'scala'

JavaファイルとScalaファイルのジョイント

ここが肝です。114行目ぐらいの

build.gralde
sourceSets.main.resources { srcDir 'src/generated/resources' }

build.gralde
sourceSets {
    main {
        scala {
            srcDirs = ['src/main/scala', 'src/main/java']
        }
        java {
            srcDirs = []
        }
        resources {
            srcDir 'src/generated/resources'
        }
    }
}

のように変更します3

サンプル

scalaでログインしたユーザーにメッセージを表示するロジックを書き、メインのクラスで登録します。

client.scala
package ex.client

// import省略

class LoginMessage(){
  @SubscribeEvent
  def onPlayerLoggedIn(event: PlayerEvent.PlayerLoggedInEvent): Unit = {
    val player = event.getPlayer
    val pos = player.getPosition
    val message = s"""
                     |Hello, World
                     |name = [${player.getName.getString}]
                     |pos  = [${pos.getX}. ${pos.getY}, pos.getZ]
    """.stripMargin
    player.sendMessage(new StringTextComponent(message), player.getUniqueID)
  }
}
testmod.java
package ex

// import省略
import ex.client.LoginMessage;

@Mod(TestMod.MODID)
class TestMod{
    public static final String MODID =  "testmod";
    private static final Logger LOGGER = LogManager.getLogger();

    public Chalcedony() {
        final IEventBus bus = FMLJavaModLoadingContext.get().getModEventBus();
        bus.addListener(this::setup);
        bus.addListener(this::enqueueIMC);
        bus.addListener(this::processIMC);
        bus.addListener(this::doClientStuff);

        MinecraftForge.EVENT_BUS.register(this);
        MinecraftForge.EVENT_BUS.register(new LoginMessage());
    }

    // ... 処理
}

参考にした記事: https://qiita.com/niwasawa/items/ca565091468abce48f53


  1. ScalaからJVMの中間言語にコンパイルすると名前に$サフィクスがつくためMod本体であるシングルトンオブジェクト(のコンパニオンクラス)のコンストラクタを参照できずIllegalAccessExceptionが投げられるようです。
    参照: https://stackoverflow.com/questions/17401565/instantiating-a-scala-class-using-reflection-javas-newinstance
    参照: https://github.com/scala/bug/issues/3276 

  2. 追加でScalaに対応するMDKやScorgeなどがあるようですが導入しなくても開発できそうです。 

  3. src/main/java 以下のファイルをJavaではなくScalaとして読み込むようにしています(Scalaは単純にJavaの拡張であるためJavaのコードははScalaとして解釈可能です)。これによりパッケージの依存関係を解決しています。 

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4