takecx / RemoteControllerMod

Minecraft Remote Controller Mod
8 stars 5 forks source link

pythonから操作できるライブラリがあってもいいかも #7

Open takecx opened 3 years ago

takecx commented 3 years ago

pythonからMinecraftを操作できるものは以外と多くないぽい

サーバーではない手元の1.13より新しいバージョンの環境で動くライブラリがあったら便利かもしれない

takecx commented 3 years ago

PyPiに登録する方法

https://qiita.com/shonansurvivors/items/0fbcbfde129f2d26301c

takecx commented 3 years ago

pythonでwebsocket

https://www.raspberrypirulo.net/entry/websocket-server

Naohiro2g commented 3 years ago

Minecraft Pi (MCPI) 0.1.0あるいは、Java版MC1.12.2以前とScratch / Pythonの接続について

ラズパイの場合:

mcpiモジュールからMCPIへソケット接続、Scratch2MCPIからScratchのリモートセンサープロトコルサーバーへソケット接続、Minecraft Pi Edition(MCPI) とScratch1.4が双方向につながる。Pythonでもリモコン可能。Java 版を動かす場合は、Forgeで入れたRaspberryJamModによるソケット通信に接続、Scratch / Pythonでリモコンできる。

(MCPI -  ソケットサーバー) - mcpi - (ソケットサーバー - Scratch 1.4)
                   |
                   +-- mcpi -  Pythonアプリ(MCと別マシンでも良い)

PCの場合:

Java版にForgeで入れたRaspberryJamModによるソケット通信にmcpiで接続、Pythonでリモコンできる。Scratch2MCPIを使うとScratch1.4にもブリッジできる。

(MC Java - Forge - mod ソケットサーバー) - mcpi - (ソケットサーバー - Scratch 1.4)
                               |   |
                               |   +-- mcpi - Pythonアプリ(MCと別マシンでも良い)
                               +-- Scratch 3 (modがRemoteControllerの場合)(別マシンでも良い)

RemoteControllerMod登場後は、RaspberryJamModの代わりに使って、Scratch3、Scratch1.4、Pythonでつながる。

Naohiro2g commented 3 years ago

1.13以降の場合

ブロックIDのフラット化(整数のインデックスによる指定から名前空間:名前の文字列指定へ)。 mod側の変更:ソケット通信からWeb Socketへの変更。

MC Windows10版=Bedrock版

MC Win10 - Code Connection(Web Socketサーバー) - MakeCode

wsクライアントが標準装備。コンパニオンアプリCode ConnectionのwsサーバーにMC、MakeCodeが接続。ブロック型、javascript、Pythonで書ける環境が揃っている。

MC Java版

(MC Java - Forge - mod  Web Socketサーバー) - (機能拡張 + Scratch 3)
                                 |
                                 +-- mcpi(改) - Pythonアプリ (MCと別マシンでも良い)

remotecontrollermodのwsサーバーに機能拡張付きScratch 3が接続。 remotecontrollermodが加えてソケット通信もサービスできれば、mcpi(要改造)から接続できることになる。ただし、Scratch3機能拡張ですでにws接続に対応していること、Bedrock版での動向もあり、Python / mcpi側でws接続に合わせるほうがいいのかもしれない。mcpiは、ブロックIDのフラット化もあるので、いずれにしても要改造。

takecx commented 3 years ago

@Naohiro2g ありがとうございます! 各構成要素についてあまり理解できていない面もあるので一度じっくり考えてみます! 整理できたら再度コメントさせてもらいます!

Naohiro2g commented 3 years ago

@takecx お役に立てたら幸いですが、全体的に誤認を含む可能性ありますので、ご注意ください。MakeCode - Code Connection - MC Berdockのところは特に新しい要素で、Windows10版MCも持ってないので、推量多めです。Scratch3機能拡張部分、Forge-mod部分も詳細は理解できていません。

MC1.16.5+remotecontrollermodにws接続で送り込むべきjsonのデータ形式を教えていただけますか?wsクライアントで流し込んだり、mcpi改造を試してみようと思います。 例えば、chat.post APIなど。ソケット接続の場合は、'chat.post("hello")'\n というLF区切りの文字列をバイト列で流し込んでいます。

takecx commented 3 years ago

@Naohiro2g

MC1.16.5+remotecontrollermodにws接続で送り込むべきjsonのデータ形式を教えていただけますか?

APIHandler.javaに以下のように定義しています(Commandsの部分はRaspberryJamModと同じようにしていたはずです)

    // Agent
    protected static final String SUMMONAGENT = "agent.summon";
    protected static final String MOVEAGENT = "agent.move";
    protected static final String STEPFORWARDAGENT = "agent.stepForward";
    protected static final String JUMPFORWARDAGENT = "agent.jumpForward";
    protected static final String ROTATEAGENT = "agent.rotate";
    protected static final String PLAYERGETPOS = "player.getPos";
    protected static final String STARTSTAGE = "startStage";
    protected static final String MOVECAMERA = "moveCamera";

    // Commands
    protected static final String WORLDGETPLAYERIDS = "world.getPlayerIds";
    protected static final String GETBLOCKWITHDATA = "world.getBlockWithData";
    protected static final String SETBLOCK = "world.setBlock";
    protected static final String SETBLOCKS = "world.setBlocks";
    protected static final String WORLDSPAWNENTITY = "world.spawnEntity";
    protected static final String WORLDCHANGEWEATHER = "world.changeWeather";
    protected static final String WORLDCHANGEGAMEMODE = "world.changeGameMode";
    protected static final String WORLDCHANGEDIFFICULTY = "world.changeDifficulty";
    protected static final String WORLDSPAWNPARTICLE = "world.spawnParticle";
    protected static final String ENTITYGETPOS = "entity.getPos";
    protected static final String ENTITYSETPOS = "entity.setPos";
    protected static final String CHAT = "chat.post";
    protected static final String GIVEENCHANT = "giveEnchant";
Naohiro2g commented 3 years ago

えーと、データ形式は変わっていないということでしょうか? Bedrock版のwsではjsonのようなので、勝手にjsonになったかと思いこんでいました。

--> Pythonからws接続でchat.postが動きました。 ws.send(b'chat.post(' + input_data.encode() + b')\n')

takecx commented 3 years ago

えーと、データ形式は変わっていないということでしょうか?

説明不足でした!おっしゃる通り受け付けるデータ形式は変わっていません!

takecx commented 3 years ago

@Naohiro2g

remotecontrollermodが加えてソケット通信もサービスできれば、mcpi(要改造)から接続できることになる。ただし、Scratch3機能拡張ですでにws接続に対応していること、Bedrock版での動向もあり、Python / mcpi側でws接続に合わせるほうがいいのかもしれない。mcpiは、ブロックIDのフラット化もあるので、いずれにしても要改造。

RemoteControllerModという名を冠しているのでmod側で可能な通信オプションを増やす方向でまずは進んでみようかと思います。つまり、modにソケット通信の機能を追加しようかと思います。 その後、mcpiのWebSocket版を開発しても良いかなと思っています。(IDのフラット化にも対応する予定)

takecx commented 3 years ago

@Naohiro2g

15 でソケット通信機能を追加しました!

ポート番号は14712です! 試してみていただけると幸いです! 🙇

Naohiro2g commented 3 years ago

ソケット通信版、pythonで試してみました。

chat.postは動き、日本語も通ります。しかし、setBlockがうまくいきません。Web Socket版でも成功しておらず、データ形式の不整合??と思っています。 world.setBlock(0,70,0,GOLD_BLOCK) あるいは、minecraft:GOLD_BLOCKなどで試しました。

ちなみに、APIHandler.java中のndler.java以下のコード、なぜarg_content[5]なのでしょうか?

            else if (cmd.equals(SETBLOCK)) {
                String[] arg_content = args.split(",");
                BlockPos targetPos = new BlockPos(Integer.parseInt(arg_content[0]), Integer.parseInt(arg_content[1]), Integer.parseInt(arg_content[2]));
                Block block = ForgeRegistries.BLOCKS.getValue(new ResourceLocation(arg_content[5]));
                assert block != null;
                myWorld.setBlockState(targetPos, block.getDefaultState());
                return null;
            }
takecx commented 3 years ago

@Naohiro2g 試していただきありがとうございます。

しかし、setBlockがうまくいきません。Web Socket版でも成功しておらず、データ形式の不整合??と思っています。 world.setBlock(0,70,0,GOLD_BLOCK) あるいは、minecraft:GOLD_BLOCKなどで試しました。 ちなみに、APIHandler.java中のndler.java以下のコード、なぜarg_content[5]なのでしょうか?

world.setBlockは現状以下の引数をとるようにしていました。

world.setBlock(x, y, z, blockID, blockData, itemID)

上記のようになっているのは、もともとはScratch側のコードを1.12.2版と1.16.5版で同じものを使用するつもりだったからなのですが、現状はScratch側は1.12.2版と1.16.5版でそれぞれ別ブランチで管理しているので、各バージョンに最適化した形で書き換えたほうが良さそうですね 🤔

そもそもScratchから操作することしか想定していなかったので上記のような状態ですが、いろんなものから操作される可能性があると考えるとコードも整理していったほうがいいですね(Wikiにドキュメントも書かないといけないと思っています)

Naohiro2g commented 3 years ago

ForgeMdkとGradleをインストールして、.jarをビルド、modsフォルダに入れて動かせるようになりました。Javaが全然なので、ここから何かできるか、、わかりませんが。

残念ながら、setBlockはまだ動かず。。 ここのルックアップがうまくいってないようで、nullチェックを外したら空気が置けました。WebSocket、ソケット両方なので謎。 Block block = ForgeRegistries.BLOCKS.getValue(new ResourceLocation(arg_content[5]));

takecx commented 3 years ago

@Naohiro2g

ForgeMdkとGradleをインストールして、.jarをビルド、modsフォルダに入れて動かせるようになりました。

おー!開発環境をインストールしていただいた感じですね!

Javaが全然なので、ここから何かできるか、、わかりませんが。

私もJavaはこのmodで初めて触っているのでわからないことだらけですが、意外となんとかなっています

world.setBlock(x, y, z, blockID, blockData, itemID)

上記の引数を受け付けるこれまでのバージョンについては、以下のコードでブロックを置くことはできていました!(1.16.5)

import socket
ip1 = 'localhost'
port1 = 14712
server1 = (ip1, port1)

socket1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket1.connect(server1)
socket1.send(b'world.setBlock(100,100,100,0,0,minecraft:granite)')

image

1.16.5ではblockIDblockData引数は使っていないのでどんな値を入れても問題ありません。 itemIDminecraft:graniteを指定しています。

Naohiro2g commented 3 years ago

謎が解けました。 マッチできなかったのは、全部小文字というのとスペースが許されない、という理由でした。 Integer.parseint()は、スペース入りだと整数化できないのかー。

スペースをトリムしても良いけど、自動化すれば問題ないので、放置。setBlocksのバグを取ってプルリク送りました。

Naohiro2g commented 3 years ago

「Pythonから操作できるライブラリ」、ということで、mcpiのフォーク版のmcpi2からさらにフォーク、mcjeとしてサンプルコードと共に公開しています。https://github.com/Naohiro2g/minecraft_remote

同じコードをラズパイ版、RemoteControllerMod 1.12.2用、1.16.5用と切り替えて使える仕組み付き。公開されている古いコードを持ってきて最新版マイクラで使ったり、これから作られる新しいコードを「生きた化石」のラズパイ版でも使える様に意識してもらうという想定です。

もしかすると、マイクラのバージョン値だけセットすればパラメーター類含め挙動を変えられる様にした方がいいな。となると、モジュール名はmcjeじゃなくてminecraft_remoteにするかも。 整理できたらPypiに登録しつつ、改良します。

これがちょっと気になってる。。まぁ、登録商標だし当然か。 https://pypi.org/project/minecraft/ "This is not a usable Python package, but the name is reserved by Microsoft Corporation."

spacewalkerjp commented 5 months ago

@Naohiro2g さん:素晴らしいライブラリの作成感謝いたします。

娘のプログラミング教育のために、このリポジトリを発見し試してみました。Windows11, Python 3.10, Forge 1.16.5 (jave version)でremotecontrollermod-1.16.5_0.05.jarを入れ、Naohiro2gさんのmcjeを試しました。具体的には、1.16.5 forge minecraftを作成し、world (creative & flat)を作成しスタート。tキーを押してminecraftのコマンドモードにし、ウインドウを非アクティブにして、windowsのターミナルからpythonスクリプトを実行するという方法です。

結果としてmc.postToChatや、mc.setBlockなどは問題なく動作するのですが、プレーヤーの現在位置から何かブロックを生成したいと考え、mc.player.getPos, mc.player.getTilePosを試そうと思いましたがここで応答が返ってきません。pythonスクリプトがその行で止まってしまいctrl + Cも受け付けない状況になります。Minecraft側を終了するとソケットが切れたということでエラーを吐いて戻ってきます。プレイヤーのentityidなどが必要かとも思いいろいろ試したのですがentityidなども取得できませんでした。

またScratch版(takecxさんの)でも試してみましたが、「言う」とか「ブロックを置く」は動作しましたが「現在位置を調べる」が何も返ってきませんでした。logsフォルダのログを見ましたが、何かエラーを吐いている様子もございませんでした。

もし何かお気づきがございましたら教えていただければ幸いです。

spacewalkerjp commented 5 months ago

追記で失礼いたします。

私の環境では新バージョン(1.16.5用のMCJE)ではgetPosが動作しないようです。

Naohiro2g commented 5 months ago

@spacewalkerjp さん、お試しいただきありがとうございます。 長期間、完全放置で申し訳ないのですが、1.16.5ではgetPos動いてないですね。多分受信自体ができてないので、Python側はずっと待っていて、mod側もおかしなパラメーターを食わされてバグっているかも。

さらに、、setBlockも、 1.13からの変更でblockID, blockDataの指定方法が変わったのですが、従来blockData部分で指定していた要素がblockID側に移動したものに関しては問題ありませんが、「ブロック状態」の指定が必用なものは対応できていません。上のやり取りでもあるように、従来の数値によるblockID, blockDataは無視されて、文字列による itemID 指定だけを通信しています。なので、デフォルトのブロック状態のものしか置けなくなっています。ブロック状態をitemIDにパックして通信できる仕様になっているのなら、Pythonモジュールmcje側での処理で解決できる可能性はあります。パックのフォーマットが違うなど。

白いウールは、問題ない。 ID: 35, Data: 0 → wool_white

オークの階段は、向きの指定が無理。itemIDにオプションをつけるとダメ。 ID: 53, Data: 0から7 → oak_stairsは良いが、oak_stairs[facing=east, half=bottom]は、ダメ。

north, south, east, west top: 逆さま、bottom: 正常

「1.13以降のブロック状態」 https://minecraft.fandom.com/ja/wiki/%E3%83%96%E3%83%AD%E3%83%83%E3%82%AF%E7%8A%B6%E6%85%8B

「1.12.2までのブロック状態」同じページ、下の方 https://minecraft.fandom.com/ja/wiki/1.12.2%E3%81%BE%E3%81%A7%E3%81%AE%E3%83%96%E3%83%AD%E3%83%83%E3%82%AF%E7%8A%B6%E6%85%8B

おまけに、1.17からはJavaのバージョンが上がったため、Forgeの開発環境にも変化があり、対応modが作れない状況になり、そのまま、解決に至っていません。1.17以降用のmodが作成できる環境自体は用意できたのですが、マイクラリモコンmodのソースの修正が必要?です。

ホントは、マイクラの最新版1.21への対応、受信APIの完備、などなどできるとよいのですが。。

spacewalkerjp commented 5 months ago

@Naohiro2g さん: ご丁寧にそして詳しく状況をご回答いただき感謝いたします。 なるほど、1.13以降にブロックに状態が付いたので大きなバージョンの壁がありそうですね。 こんなに複雑になっていたとは・・。

最新の1.21ではJAVAも21になっているようで、かなり現状と状況が変わってきていることも理解いたしました。

娘にPythonベースで制御させてあげようとおもっていますが、普段1.21で遊んでいる娘からすると少し古いマインクラフトは違和感あるようで(笑)できるだけ新しいバージョンでの制御ができるmodを探していてこちらの1.65.5(mcje)にたどり着いたという背景です。

一点質問なのですが、「マイクラリモコンmodのソース」というのはどのリポジトリになりますでしょうか?またそれはマインクラフト公式のものなのでしょうか?

Naohiro2g commented 5 months ago

そうなんですよ。できた当初は1.13を越えた唯一無二の存在で、しかもマイクラ最新バージョンだったんですが。。 直後に1.17が出て、追いつけないうちにどんどん進んでしまいました。うわー、ほんとだ。マイクラ1.20からJava21になったんですね。

ソースは、このリポtakecx/RemoteControllerModそのものです。これをビルドすると、modファイルができます。