何言ってるのかよく分からないって?
大丈夫、私もこの1週間位で習得したからまだ大層なことはできてない。
実行環境
- Windows10(64bit) (32bitでも動作はするし、Mac/Linuxでも動作するはずだけど記述とかが違う)
- Minecraft公式サーバ (プラグイン鯖でも動くはず)
- Python (3以降なら多分大丈夫)
今回用いる技術
- python (多少知識がある前提で説明をします)
- mcrcon
- threading
まず、mcrconって何?
mcrconは「MinecraftのRconを使いやすくしたライブラリ」のこと。
で、Rconは「Remote console」の略で、Minecraft内のコマンドを遠隔から実行できる機能のこと。
server.propertiesの中身を見たことある人は、
broadcast-rcon-to-ops=true
enable-rcon=false
rcon.password=
rcon.port=25575
の記述を見たことがあるかもしれないですね。
このあたりをいい感じにするとrconの接続がいい感じにできます。
mcrconで接続してみる
まず、公式Githubリンクをここに書いておきます。
https://github.com/Tiiffi/mcrcon
困ったときは公式ドキュメントを見ろ、という格言もあるくらいですからね。(ない)
mcrconを使用する準備
まずmcrconをインストールするところから。
pip install mcrcon
いつものpipですね。
Linuxとかの場合は公式ドキュメントからインストール手順を確認してください。私に調べるほどの余力はない。
python×mcrconでMinecraftサーバーを動かす準備
やりたいことをやる前に、とりあえずpythonとmcrconについて軽く勉強をしてみましょう。
少し触ればだいぶやりやすくなるぞ。
mcrconを触ってみる
今回のゴールは「pythonでmcrconの接続及び処理を行う」ですが、とりあえずmcrcon単体で接続をしてみましょう。
まず、server.propertiesの以下の部分を書き換えます。
broadcast-rcon-to-ops=false
enable-rcon=true
rcon.password=<任意の文字列>
この3行を設定すれば、準備は万端です。さぁ、Minecraftのサーバーを起動しましょう。
起動すると、以下のような表示が出ます。出なかったら設定を間違えています。
[23:27:25] [Server thread/INFO]: Thread RCON Listener started
これが出たら、Powershellあたりから以下のコマンドを実行し、mcrconで接続をします。
mcrcon localhost
(なんか環境によっては動いたり動かなかったりします 私はVSCodeのターミナルでだけ動きました なんで?)
パスワードの入力を求められるので、さっき設定した文字列を入力してください。
接続に成功すると、Minecraftのコンソールに以下の表示が出ます。
[23:28:03] [RCON Listener #1/INFO]: Thread RCON Client /127.0.0.1 started
これで接続成功です。mcrconでMinecraftのコマンド(listなど)を打つとちゃんと出力が返ってきます。
切断はCtrl+Cを使ってください。それ以外にあるかも...
※localhost以外でrconの接続を行う場合はセキュリティに十分気をつけてください
pythonでmcrconを動かす
まずpythonでmcrconを接続・切断するコマンドは
import mcrcon
mcr = MCRcon("localhost", "rconのパス") # MCRcon起動
mcr.connect() # 接続
### ここにmcrconの動作を記述 ###
mcr.disconnect() # 切断
です。ちなみにサーバーを落とすときには切断しないと長ったらしいエラーを吐かれます。
まず初めに、mcrconを用いてlistコマンドを実行して出力を取得しましょう。
mcrconの動作に以下の内容を書いて実行してください。
res = mcr.commands("list")
print(res)
pyファイルを実行すると、listコマンドを実行した結果が返ってきます。
これで「mcrconで接続してコマンドを実行するファイル」が完成しました。
ただ、今の状態だと『実行時にコマンドが実行されるだけ』なので、継続的な処理は全くできません。
pythonからMinecraftサーバーを起動する
pythonファイルに以下の記述を書いたら、pythonからサーバーを起動できます。
ついでに入出力もいい感じにできます。
server = subprocess.Popen(['java','-Xms4G','-Xmx4G','-jar','spigot-1.18.2.jar','--nogui'],stdout=subprocess.PIPE,stderr=subprocess.STDOUT,shell=True,text=True,encoding='shift-jis')
for i in server.stdout:
print(i,end="")
Popenについては自身で調べていただくとして、軽く説明もしておきます。
- 入力:Popenで開いた際に標準入力を受け取る形になる
- 出力:server.stdoutに保存されるのでforで表示させる
(エンコード方式を指定する必要があったりする。私はあった。)
起動方法は以下の記事を参照しました。
PythonでMinecraftサーバーの独自コマンドを作る
1年以上更新されてないですが、仕様はほぼ同じです。
2つの技術をつなぎ合わせるぞ
pythonからサーバーの起動ができます、pythonでmcrconを使った接続ができます。
この2つの技術をつなぎ合わせて、以下の動作を実装してみましょう。
「pythonでMinecraftサーバーを起動し、mcrconを自動接続/自動切断する」
レベル感はだいぶ上がりますが、2つの技術を習得できれば比較的簡単です。
やるぞ!(オモコロ風)
Minecraftサーバーとmcrconを同時に起動する
まず、Minecraftサーバーを起動しながらmcrconを起動する準備をします。
先ほど記述した『Minecraftサーバー起動+標準出力表示』と、『mcrconの動作』を並列で実行できるようにしましょう。
pythonの技術『threading』を使用します。
threadingに関するちょ~~~~分かりやすい説明は以下のサイトあたりに載ってます。
【Python】 並列処理を理解しよう 【threadingの使い方 01】
Pythonのthreadingとmultiprocessingを完全理解
要するに、「メインスレッドとサブスレッドを同時に処理させることができる」というわけです。
とりあえず、サーバー起動後にmcrconをサブスレッドで動作させてみましょう。
ただ、mcrconはMinecraftサーバーがrconのリッスンを開始したあとに起動する必要があるので、まずは標準出力の構文解析をする必要があります。
これがまたダルいんだ................
標準出力の構文解析(超簡易的)
今回は使用用途が「Minecraftサーバーの出力を構文解析する」だけなので、超簡易的な構文解析機能を作りましょう。
標準出力は取得できるので、それを解析してrconの接続を開始するコードは以下のとおりです。
今回追加する機能は
- 標準出力を解析し、サーバー出力を取得する
- リッスン開始に合わせてmcrconを起動する
- リッスン終了に合わせてmcrconを切断する
の3つです。
concheck = False # RCON接続確認用
disconcheck = False # RCON切断確認用
for i in server.stdout:
print(i,end="") # サーバーの出力を表示
if len(i.split()) > 3: # サーバーの出力が3単語以上の場合
# 構文解析開始
temp = i
time = re.search(r"\[\d{2}:\d{2}:\d{2}\]", temp).group() # 時間を取得
temp = temp.replace("%s " % time, "") # テキストから時間を削除
type = re.search(r"\[.*?\]", temp).group() # 種別を取得
temp = temp.replace("%s: " % type, "") # テキストから種別を削除
if concheck == False and temp == "Thread RCON Listener started\n": # RCONのListen開始
concheck = True
### ここにmcrcon起動の処理を書く ###
if disconcheck == False and temp == "Stopping server\n": # サーバーの停止
disconcheck = True
### mcrconにループ処理を記述するなら切断あたりの記述が必要
### mcrcon切断の処理は書かない(disconcheckをTrueにするだけで切断できるようにする)
長いね。仕方ないね。構文解析って手作りするとすごいダルいんだね。
内部使用については、見て分かる程度の難易度なので深堀って説明はしません。
強いて言えば正規表現の扱いが面倒というくらい。一応参考サイトを張っておきます。
Pythonの正規表現モジュールreの使い方(match, search, subなど)
これでメインスレッドの記述はほぼ終わりました。
あとはmcrconの内部動作を定義するだけです。(これもこれでそれなりに面倒)
mcrcon起動用のサブスレッドを作成する
サブスレッドとしてmcrconを起動するために、mcrcon用の外部関数を作成します。
def rcon(): # RCON動作
mcr = MCRcon("localhost") # MCRcon起動
mcr.connect() # 接続
### ここにmcrconの初期動作を記述 ###
while(not disconcheck): # メイン処理
### ここにmcrconのループ動作を記述 ###
mcr.disconnect() # 切断
あとはこれをthreadingで起動するだけです。標準出力構文解析の中にmcrcon起動の記述を書き足したものが以下の形です。
concheck = False # RCON接続確認用
disconcheck = False # RCON切断確認用
for i in server.stdout:
print(i,end="") # サーバーの出力を表示
if len(i.split()) > 3: # サーバーの出力が3単語以上の場合
# 構文解析開始
temp = i
time = re.search(r"\[\d{2}:\d{2}:\d{2}\]", temp).group() # 時間を取得
temp = temp.replace("%s " % time, "") # テキストから時間を削除
type = re.search(r"\[.*?\]", temp).group() # 種別を取得
temp = temp.replace("%s: " % type, "") # テキストから種別を削除
if concheck == False and temp == "Thread RCON Listener started\n": # RCONのListen開始
concheck = True
rconth = threading.Thread(target=rcon,args=())
rconth.start()
if disconcheck == False and temp == "Stopping server\n": # サーバーの停止
disconcheck = True
### mcrconにループ処理を記述するなら切断あたりの記述が必要
### mcrcon切断の処理は書かない(disconcheckをTrueにするだけで切断できるようにする)
これでmcrconの起動と並列処理ができました。
とりあえず今回はここまでできれば十分でしょう。
お疲れさまでした。
まとめ
今回の内容は、
- pythonでmcrconを動作させる
- pythonでMinecraftサーバーを起動する
- Minecraftサーバーの出力を取得し、解析する
- Minecraftサーバーとmcrcon動作を並列動作させる
の3点でした。
このあたりを超有効活用すれば、ゲームっぽい動作をさせることができます。
dataコマンドでインベントリを取得して色々情報を得たり、scoreboardコマンドで色々と処理をさせてtellrawで表示したり...
Minecraftの標準出力をmcrconに渡す方法もあるのですが、面倒なので気が向きに向けば書きます。
ちなみにmcrconを使うと普通のコマンドだけだととても難しい動作を記述できます。
例えば、プレイヤーネームの取得(listコマンドの出力を解析する)とか、stringの扱い(正規表現や配列で自由な操作を行う)とかができます。
ちなみにpythonで書いた一連のファイルをpyinstaller等でexe化すれば、起動するだけでサーバー起動~データパック的動作ができるexeファイルが完成します。
夢はでっかいぞ。