前提
minecraft serverにおけるコマンドをdiscord bot経由で入力することを想定しています.
そして,
- WSL, Ubuntu 22.04.1
- Python 3.10.6
- papermc
という環境で行っています.また,minecraft serverはsubprocess
による起動で実行しているため,この知識も多少必要です.stdout
とstdin
がわかっていれば大丈夫ではないでしょうか.
次に,私の環境のディレクトリ構成は以下のような感じです.
.
├── mcserver
│ └── logs
│ └── latest.log
└── qiita_mcmanager
├── MCServer.py
└── test-getlog.py
もちろん,ファイルやディレクトリはほかにもありますが,今回利用するものはこの辺だけです.
MCServer.py
はminecraft serverの起動や停止などを行うクラスで,test-getlog.py
はminecraft server起動用のテストとして作成しているファイルです.
test-getlog.py
test-getlog.py
は以下のようなプログラムです.
from MCServer import MCServer
import time
server = MCServer()
server.start()
time.sleep(1)
print(server.get_command_log("list"))
time.sleep(1)
server.stop()
特に特筆するところはないです.強いて言うなら,serverを起動させた直後にコマンド入力させるのはちょっと不安だったのでsleepさせてるところでしょうか.まあ重要じゃないです.
コマンド結果を取得
現在のログにおける最後の行を確認しておきます.続いて,コマンドを入力した直後にログファイルを読み込みます.そして,確認済みのところまで一行ずつ戻ることでコマンドの結果が取得できます.
コードは以下の通りです.
def get_command_log(self, cmd: str) -> list[str]:
if (self.process is None):
print("server is not active")
return
if (self.process.poll()):
print("server is already stopped")
return
log_file_path = "../mcserver/logs/latest.log"
last_log = ""
with open(log_file_path, "r") as log_file:
logs = log_file.readlines()
last_log = logs[-1]
self.process.stdin.write(f"{cmd}\n".encode("utf-8"))
self.process.stdin.flush()
time.sleep(0.5)
command_logs = []
with open(log_file_path, "r") as log_file:
logs = log_file.readlines()
while (logs[-1] != last_log):
command_logs.append(logs.pop(-1))
return command_logs[::-1]
複雑なことはしていないと思います.簡単に流れを説明すると,
- ログファイルを開いてファイルの中身を行ごとに分けて読み込む
-
open()
とreadlines()
-
-
last_log
に最後の行を保存 - コマンドの入力
- ログに書き込まれるまで少し待機
- 改めてログファイルを開き,
last_log
の行が現れるまでcommand_logs
にログを入れる - 末尾から入れているのでreverseして返す
という感じです.前回記事のようになんかごちゃごちゃ工夫しなくてできますね.徹夜はなるべくやめましょう.
time.sleep()
で待機させていますが,これをするとdiscord botがその秒数分止まります.minecraft serverは別スレッドで動いていますし,簡単に確認してみたところ特に変な挙動はしていませんでした.ワールドが重くなるとどうなるかわからないため,asycio.sleep()
に変更した方が良いかもしれません.ただ,私はasync/await
に翻弄されたので諦めました.
以上がminecraft serverに入力したコマンドを取る方法でした.
お疲れ様でした.