Minecraft Command Advent Calendar 2024 14日目の記事です
はじめに
バニラ鯖でも外部通信、したくないですか?
今回は外部と通信する方法をいくつかまとめたので解説していきます
基本的に外部ツールが必須となっています
早見表
方法 | 双方向性 | リアルタイム性 | シングルプレイ | 外部ツール | 転送可能データ量 | 備考 |
---|---|---|---|---|---|---|
RCON | 〇 | 〇 | ✖ | 必要 | 多い | ログがかなり荒れる |
チャットログ | △ | ◎ | ✖ | 必要 | 多い | ゲーム内チャットに表示される |
名前付きモブ | △ | ◎ | ✖ | 必要 | 多い | ログが若干荒れる |
nbtファイル | △ | △ | 〇 | 必要 | とても多い | |
スキンデータ | ✖ | ✖ | 〇 | 不要 | とても少ない |
解説
RCON
バニラ鯖で通信すると言えばこれ。
使い方
server.properties
でenable-rcon=true
に設定、サーバーアドレスとrcon.password
とrcon.port
に指定した値を接続ツール等に入れることで接続できる
コマンドが実行できるため基本的に何でもできる
ループ処理でリアルタイムに値を取得すると言ったことが可能
ただしtickを超える頻度でのアクセスを行った際の挙動は不明
実装例
Pythonのmcrconを使用
コード
from mcrcon import MCRcon
with MCRcon("127.0.0.1", "test") as mcr:
resp = mcr.command("/scoreboard players get #data data")
print(resp)
レスポンス
#data has 1 [data]
チャットログ
使い方
sayコマンド等のチャットログによる出力を利用
実装例
コード
送信用function
# データ送信用のモブを召喚
summon minecraft:marker 0 0 0 {Tags:["message"],CustomName:'"#message"',NoAI:1b,Silent:1b}
# マクロ呼び出し
execute positioned 0 0 0 as @n[type=marker,tag=message] run function message:say/macro with storage message
# kill
execute positioned 0 0 0 run kill @n[type=minecraft:marker,tag=message,distance=..0.1]
マクロ
$say $(data)
ログ読み取り(python)
import os
import re
current_line = ""
while True:
# ログのリアルタイム読み取り
with open('logs/latest.log', 'rb') as f:
try: # catch OSError in case of a one line file
f.seek(-2, os.SEEK_END)
while f.read(1) != b'\n':
f.seek(-2, os.SEEK_CUR)
except OSError:
f.seek(0)
last_line = f.readline().decode().rstrip("\n")
# 最終行が更新されていたら
if last_line != current_line:
# 正規表現で名前付きMOBの死亡メッセージを抽出
match_line = re.search(r"(?<=\[\d\d:\d\d:\d\d] \[Server thread/INFO\]: \[Not Secure\] \[\#message\] ).*(?=)", last_line)
if match_line:
print(match_line.group(0))
# 現在行を更新
current_line = last_line
実行例
/data modify storage minecraft:message data set value {"test":[1,2,3,4]}
/function message:say
レスポンス
{test:[1,2,3,4]}
名前付きモブ
使い方
名前付きのモブをkillした際にログとして出力される挙動を利用
それなりに長めのデータも出力できる
実装例
コード
送信用function
※0 0 0に看板を設置します
# データ格納用のモブを召喚
summon minecraft:allay 0 0 0 {Tags:["message"],CustomName:'""',NoAI:1b,Silent:1b}
# 一旦看板へデータを書き込む
setblock 0 0 0 air
setblock 0 0 0 oak_sign{front_text:{messages:['{"storage": "message","nbt":"data"}','""','""','""']}}
# モブのCustomNameへデータを代入
execute positioned 0 0 0 run data modify entity @n[type=allay,tag=message,distance=..0.1] CustomName set from block 0 0 0 front_text.messages[0]
# データの2重出力対策
execute positioned 0 0 0 run damage @n[type=minecraft:allay,tag=message,distance=..0.1] 9999 message:nolog
# 看板を撤去
setblock 0 0 0 air
ダメージタイプ
{
"message_id": " ",
"exhaustion": 0,
"scaling": "never"
}
ログの読み取り(python)
import os
import re
current_line = ""
while True:
# ログのリアルタイム読み取り
with open('logs/latest.log', 'rb') as f:
try: # catch OSError in case of a one line file
f.seek(-2, os.SEEK_END)
while f.read(1) != b'\n':
f.seek(-2, os.SEEK_CUR)
except OSError:
f.seek(0)
last_line = f.readline().decode().rstrip("\n")
# 最終行が更新されていたら
if last_line != current_line:
# 正規表現で名前付きMOBの死亡メッセージを抽出
match_line = re.search(r"(?<=\[\d\d:\d\d:\d\d] \[Server thread/INFO\]: Named entity ...\[').*(?='/\d*. l\='ServerLevel\[.*\]', x\=\d*.\d*, y\=\d*.\d*, z\=\d*.\d*\] died: death.attack. )", last_line)
if match_line:
print(match_line.group(0))
# 現在行を更新
current_line = last_line
実行例
/data modify storage minecraft:message data set value {"test":[1,2,3,4]}
/function message:send
レスポンス
{test:[1,2,3,4]}
NBTファイル
使い方
ワールドデータ内にあるscoreboard.dat
やcommand_storage_*.dat
を外部から読み込んで値を取得
ワールドを閉じるかsave-all
を実行するとファイルが更新される
pythonであればNBTのようなライブラリで読み書きができる。
スキンデータ
使い方
プレイヤーのスキンデータを利用したデータの取得
まとめ
現状、RCONとログの読み取りによる送受信が最適解と思われる