MinecraftConnection 3.0.0 のベータ版をリリースしました

2025/11/09 公開 (2025/11/09 更新)

ついに、MinecraftConnectionのアプデを行いました!
NuGet を見て驚愕したのが、なんと最終アプデが2023年8月だったんですよね。この間に .NET のバージョンは上がるし、マイクラもアプデするしで色々と環境の変化がすごかったです。
特に、マイクラのバージョンが 1.20.5 を超えたあたりから NBT 構造が変わっていて、それまでに使っていた文字列変換用のメソッドや、一部の機能が使えなくなっていて焦りました。こりゃアプデせなアカンなと。

大きく変えた点について

ざっくりまとめるなら、利用者側としては

  • メソッド名をマイクラのコマンドに近づけた
  • DataGet / DataModify メソッドを追加
  • 非同期なメソッドの追加

開発者側としては

  • パケット送信の非同期化
  • Entity基底クラスの作成
  • NBT変換用のクラスの作成

こんな感じでしょうか。特に、メソッド名をマイクラのコマンドに近づけたのは良かったと思います。

バージョンごとの書き方の違い

例えば花火を打ち上げる方法についてですが、2.X系のバージョンでは下記のように実装します。

using MinecraftConnection;
using MinecraftConnection.Entity;

string address = "127.0.0.1";
ushort port = 25575;
string pass = "minecraft";
MinecraftCommands command = new MinecraftCommands(address, port, pass);

Position pos = new Position(-516, 64, -205);
Fireworks fireworks = new Fireworks()
{
    LifeTime = 30,
    Type = FireworkType.LargeBall,
    Colors = FireworkOption.RandomColor(),
    FadeColors = new List<FireworkColors> { FireworkColors.WHITE },
};
command.SetOffFireworks(pos, fireworks);

対して、3.X系のバージョンではこのように書きます。

using MinecraftConnection;
using MinecraftConnection.Entity;

string address = "127.0.0.1";
ushort port = 25575;
string pass = "minecraft";
using var command = new MinecraftCommand(address, port, pass);

var pos = new Position(-516, 64, -205);
var fw = new FireworkRocket
{
    LifeTime = 30,
    Shape = FireworkShape.LargeBall,
    Colors = FireworkOption.GetRandomColors(),
    FadeColors = new List<FireworkColor> { FireworkColor.WHITE },
};
command.Summon(fw, -14, 63, -17);

変わった点をまとめるとこんな感じです。

  • MinecraftCommands クラスから MinecraftCommand クラスへの名称変更
  • MinecraftCommand の IDispasable インターフェース実装
  • Fireworks クラスから FireworkRocket クラスへの名称変更
  • 花火クラスの Type から Shape へのプロパティ変更
  • SetOffFireworks メソッドから Summon メソッドへの名称変更

名称変更について

複数形では複数のコマンドを実行するインスタンスかと間違える可能性がありそうなのと、インスタンス生成した場合にそれ自体は個別なのでふさわしくないと思いました。クラス内に定義されたコマンドは複数ありますが、そのクラス自体は単数形なのでちょっと違和感を感じました。

また、Fireworks クラスの名前を変えた理由としては、マイクラのアイテム名に合わせるためですね~。このライブラリ特有の名前にするより、もともとマイクラを知っている人が違和感を感じないよう、なるべくマイクラの名称にあわせました。
その意味でいうと、Summon コマンドもそうですね。花火を打ち上げるときも summon コマンドを使うわけですから、合わせるべきかなと。

名称の話とはちょっと違うのですが、Entity クラスを作って FireworkRocketPlayer クラスに継承しており、Summon メソッドの引数を Entity とすることで、エンティティ属性を持つ派生クラスを引数に渡すことができるようになりました。つまり、Summonメソッドにエンティティ情報を渡したい、使いたい場合は生成したクラスに Entity クラスの継承が必要になるということです。むしろ、このほうがメソッドを再利用できて便利ですね!

インスタンスの解放

MinecraftCommand クラスに IDisposable インターフェースを実装しました。これにより、コマンドを実行した後にリソースが解放されるようになります。

そもそも、このライブラリは下記の順番で動きます。

  1. インスタンス生成時にマイクラサーバーへ接続(アドレスとパスワードを使用)
  2. サーバーへコマンドを送信
  3. サーバーでコマンドを実行
  4. 実行結果のレスポンスを受け取る
  5. サーバーとの接続を閉じる

以前のライブラリでは接続を閉じても、インスタンスのリソースは解放していませんでした。今回は using か、Dispose() を用いることによってリソースを解放できるようになりました。

非同期メソッド

今回から非同期メソッドを追加しました。例えば、DataGetEntity を使用してプレイヤーの位置情報を取得するときですが、同期と非同期を選べます。

var command = new MinecraftCommand("127.0.0.1", 25575, "minecraft");

var data = await command.DataGetEneityAsync<Player>("takunology");
var data = command.DataGetEneity<Player>("takunology");

ちなみに、データを取得する際にはジェネリックを使用しており、そのデータがどのエンティティのNBTか、属性をあわせる必要があります。ここではプレイヤーのデータを取得していますので、Playerクラスを使用します。

実は、GitHub の Issue に非同期メソッド使えないか投稿があったんですよね。ただ、サーバー側では順番に実行されるので非同期にする意味あるんかなーと思っていました。あったんですよね、これが。非同期って別にサーバー側だけでなくアプリケーション側で非常に重要なんですよね~。
特にGUIアプリを作る場合はUIスレッドと別で実行するので、処理が完了するまでUIが操作できない(いわゆるデッドロック)が発生します。

非同期にしたことで、例えばこんなアプリケーションが作れるようになりました。プレイヤーの位置情報をリアルタイムでプロットするアプリです。ここでは Windows App SDK (WinUI3) を使用しています。

<Window
    x:Class="GuiApp.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:GuiApp"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Title="GuiApp">

    <Window.SystemBackdrop>
        <MicaBackdrop />
    </Window.SystemBackdrop>

    <Grid>
        <Canvas x:Name="MotionCanvas" Background="White"/>
    </Grid>
</Window>
using MinecraftConnection;

public sealed partial class MainWindow : Window
{
    private List<Position> playerMotions = new List<Position>();
    public MainWindow()
    {
        InitializeComponent();

        CompositionTarget.Rendering += OnRender;
        StartMotionUpdate();
    }

    private async void StartMotionUpdate()
    {
        var command = new MinecraftCommand("127.0.0.1", 25575, "your-pass");
        while (true)
        {
            var data = await command.DataGetEntityAsync("player-name");
            var info = data.Position;

            DispatcherQueue.TryEnqueue(() =>
            {
                if (playerMotions.Count > 200)
                {
                    playerMotions.RemoveAt(0);
                }
                playerMotions.Add(info);
            });
            System.Diagnostics.Debug.WriteLine($"X:{info.X} Y:{info.Y} Z:{info.Z}");
            await Task.Delay(5);
        }
    }

    private void OnRender(object? sender, object e)
    {
        MotionCanvas.Children.Clear();

        double canvasWidth = MotionCanvas.ActualWidth;
        double canvasHeight = MotionCanvas.ActualHeight;
        double scale = 20;

        foreach (var motion in playerMotions)
        {
            var ellipse = new Ellipse
            {
                Width = 5,
                Height = 5,
                Fill = new SolidColorBrush(Windows.UI.Color.FromArgb(255, 255, 0, 0))
            };

            double x = canvasWidth / 2 + motion.X * scale;
            double y = canvasHeight / 2 - motion.Z * scale;

            Canvas.SetLeft(ellipse, x);
            Canvas.SetTop(ellipse, y);
            MotionCanvas.Children.Add(ellipse);
        }
    }
}

動かすとこんな感じ。

おわりに

MinecraftConnection はまだまだ調整中で、ほんの一部の機能を実装したベータ版としてリリースしました。特にエンティティクラスのジェネリックを準備するのが大変で、まだまだ時間がかかりそうです。

使ってみたい方はぜひ下記のNuGetからどうぞ。まだドキュメントが追いついていないので、引数などを参考にしてみてください!

https://www.nuget.org/packages/MinecraftConnection/3.0.0-beta

たくのろじぃ

本プロジェクトの企画・運営担当。ChatGPTといっしょにWebサイトを立ち上げ、マイクラプログラミングに関する記事を執筆中。趣味は温泉旅行とプログラミング。