[管理篇] 如何對 Tmux 中的 Minecraft 伺服器下指令

為了讓 Minecraft 可以持續的執行,我們常會使用 Tmux 等終端機工具。不過各位指令的魔術師們,是否想過可以透過腳本來與 Tmux 中的程式互動呢?

這篇文章中,我們會逐步介紹 tmux 的機制,並使用一個簡單的腳本,打造一個顯示系統時間的時鐘!

一個簡單的範例

首先像平常一樣,開啟一個 tmux session:

tmux

然後開一個新的終端機視窗,用 list-sessions 確認目前已經開啟的 session:

tmux list-sessions
tmux ls # 也可以縮寫成 ls

有趣的地方來了,我們其實可以透過 send-keys 功能,傳送 ls 指令到先前的 tmux 裡面:

tmux send-keys ls ENTER

上面指令中的 ENTER 就是按下 Enter 鍵的意思。

以下列出幾個常用的按鍵(不分大小寫):

  • Escape Esc 鍵
  • Enter Enter 鍵
  • Tab Tab 鍵
  • DeleteDC: Delete 鍵
  • C-c: Control + c
  • M-1: Alt + 1

想確認所有支援的按鍵可以從原始碼的 key_string_table 裡面找,組合鍵(Modifier)的部份可以看看 key_string_get_modifiers() 函式。

注:Shift 組合鍵的部份比較複雜,因為 Shift 鍵本身並不是一個通用的組合鍵(general modifier),它只能與 F1~F12 和 方向鍵搭配使用。舉例來說 S-a 並不會送出大寫的 A而是不存在對應的按鍵。

換句話說,鍵盤上 Shift 鍵的意義,比較接近筆電鍵盤上的 Fn 鍵。當我們按下 Shift+a 的時候,送出的並不是一組按鍵(Shift+a),而是一個按鍵訊號(A)。

可以參考以下這篇討論中,nicm 的解釋: send-keys command with shift modifiers doesn’t work? · Issue #309 · tmux/tmux

使用 -t 指定 session

那如果我們開啟了兩個 tmux 呢? send-keys 怎麼知道要送到哪裡?

它當然是不知道的。

以下這個範例,我們在左邊的終端機中,開啟兩個分頁,分別執行 tmux(開啟了兩個 tmux 的 session)。並在右邊的終端機中,送出 send-keys 指令:

結果是第二個分頁(較新的分頁)收到這個指令,這種不確定的特性可不適合寫腳本的時候使用啊!

好消息是,我們可以指定自訂的 session 名稱,並讓 send-keys 把按鍵傳送給它!

send-keys [-HlMRX] [-N repeat-count] [-t target-pane] key ...
              (alias: send)

首先請改用 new-session 指令,並加上 -s 參數指定 session 名稱(本例為 mySession):

tmux new-session -s mySession

透過 list-sessions 指令,我們可以發現剛才的 1 號 session 的位置,這次的名子是 mySession

透過 -t 選項,我們可以指定把 ls 指令送到 mySession 裡面:

tmux send-keys ls ENTER               # 送到最新的 session
tmux send-keys -t mySession ls ENTER  # 送到 mySession

注:在本文寫作的當下 send-keys 指令在 -t 0 的時候,判斷會失準,導致按鍵被送到錯誤的 session,如果你也遇到同樣的問題,可以用以下指令重新命名 session:

tmux send-keys -t 0 "t0" ENTER

較為複雜的 session

這邊為了方便理解,我們開一個乾淨的 session 來解釋。

首先開啟一個新的 session:

tmux new-session -s mySession

並且開啟複數的 windows 和 panel,第一頁放了 Minecraft Server 和 htop 等工具,第二頁則是一個 shell,方便進行設定檔的修改。

注:tmux 預設按鍵綁定如下(部份):

  • 按下 Control + b 之後的按鍵視為 tmux 命令(稱為 prefix
  • prefix + % 垂直分割 window (左右各半)
  • prefix + " 水平分割 window (上下各半)
  • prefix + $ 重新命名 session
  • prefix + , 重新命名 window

將第一個 window 重新命名為 server_dash,第二個 window 重新命名為 files

tmux rename-window -t mySession:0 server_dash
tmux rename-window -t mySession:1 files

可以從終端機下方的狀態列看出改變:

舉例來說,假設我們要 傳入指令到 Minecraft Server ,可以透過以下的指令來達成:

tmux send-keys -t mySession:server_dash.0 list ENTER

意思是要重送到 mySessionserver_dash第 0 個 panel 中。

一個範例:現實時間的時鐘

這邊使用一個橡木告示牌顯示時間,透過 Server 終端機中執行 setblock 指令,將外部的時間傳入:

setblock 242 67 -96 oak_sign[rotation=8]{Text1:'',Text2:'"Time"',Text3:'"01:01"'}
  • 242 67 -96 是方塊的座標 X/Y/Z
  • oak_sign 指定橡木告示牌
  • [rotation=8] 因為告示牌的文字只有在其中一面,所以把它轉過來
  • {Text1:'',Text2:'"Time"',Text3:'"01:01"'} 告示排上的文字,第一行空白

有時 setblock 不能重複指定相同的方塊,會出現如下的錯誤:

[Server thread/INFO]: Could not set the block

所以更新時間前必須先設定該方塊為空氣:

setblock 242 67 -96 air

編寫成腳本:

#!/bin/bash

while true; do
  # 設為空氣
  tmux send-keys -t mySession:server_dash.0 "setblock 242 67 -96 air" ENTER
  # 設為告示牌
  tmux send-keys -t mySession:server_dash.0 "setblock 242 67 -96 oak_sign[rotation=8]{Text1:'',Text2:'\"Time\"',Text3:'\"$(date "+%X")\"'}" ENTER
  # 暫停一秒
  sleep 1
done

加上執行權限:

chmod u+x sys_time.sh

執行之後會看到這個告示牌不斷的顯示最新的時間了!

注:如果畫面上的文字輸出很煩人的話,可以關掉 sendCommandFeedback

/gamerule sendCommandFeedback false

參考資料:

發表迴響

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料