# 近況報告(Steampunk編)
この記事は、[みす老人会 Advent Calendar 2024](https://adventar.org/calendars/10110)の12/12の記事です。
こんにちは。55代のELICXIRです。皆様お元気でしょうか。
なんで12日に公開する記事を12日になってから書き始めているのか...コレガワカラナイ
## Minecraft Steampunk で遊ぼう
最近はMinecraftの[Steampunk](https://www.curseforge.com/minecraft/modpacks/steam-punk)というmodpackで遊んでいます。いわゆる工業化mod系のmodpackというやつです。
### modとは?
ゲームの仕様を変更したり新要素を追加したりするやつ 。modificationってこと
### steampunkの特徴とは?
歯車やシャフトを組み合わせて様々な機械を作れる工業化modである[create](https://www.curseforge.com/minecraft/mc-mods/create)をベースに様々な冒険要素が追加されているぞ!
この動画を見ればcreateの大体の雰囲気はわかる
<iframe width="560" height="315" src="https://www.youtube.com/embed/rR8W-f9YhYA?si=jpvDddxusHKaQGc2" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
加えて、温度や大気の状態についても新しく概念が導入されて寒すぎたり空気が薄すぎたりすると死ぬぞ!
(つまりいきなりブランチマイニングしてダイヤを集めることは不可能)
強力な敵が多数跋扈しており、無計画だとすぐ死ぬぞ!
## イカれた仲間を紹介するぜ
ELICXIR (サーバー管理者)
biraki
SUAN
の3人体制でやっております。
## イカれた世界をのぞいてみようの会
容量的に動画を張るのは厳しいので画像になってしまった...
気になったものがあったら君もSteampunkを導入してたしかみてみろ!
### create名物:火打石で埋まったチェスト
石材系の自動化副産物として火打石が大量に出るが....使い道がいまいちないためこのように大量にチェストの中に放置されているのを見かける....
<img src="https://hackmd.io/_uploads/S1GRp0DVJe.png" width="80%">
### 指つんつん植林マシーン
中央の柱を軸に回転しつつ木の伐採と植林を同時に行う
<img src="https://hackmd.io/_uploads/ryvjn0wN1e.png" width="80%">
### 指つんつん土製造マシーン
砂利x2+土x2→粗い土x4
粗い土x1をクワで耕すと土x1になる
つまり....?????
実質的には砂利を土に変換できてるってワケ
それを行う機械がこちら
動画でお見せできないのが残念です....
<img src="https://hackmd.io/_uploads/BkKhWJd4Jx.png" width="80%">
### 指つんつん汎用マシーン
ベルトコンベアでアイテムを流してそこに働きかけることができる
(画像では原木に対して斧を用いることで樹皮をはいだ原木を自動で生成している)
<img src="https://hackmd.io/_uploads/r1qQykOEJe.png" width="80%">
<img src="https://hackmd.io/_uploads/S1j811u4yx.png" width="80%">
### さっきから出てくる「指つんつん」って?
これです。これに動力を与えることでプレイヤーが行う道具の使用等の各種操作を行わせることができます。(動力を与えると高速でこの指がつんつんします。)
<img src="https://hackmd.io/_uploads/rygjGkdEkl.png" width="50%">
### 肥沃な土の製造風景
肥沃な土を用いることで作物を効率的に育てることができる。
特殊な土を置いて放置すると肥沃な土ができる。キノコを植えると変化速度が速くなるらしいのでやってみた
<img src="https://hackmd.io/_uploads/H1fv7yO4Jl.png" width="80%">
### スチームエンジン工場
スチームエンジンを動かすための動力(となる作物)の収穫からすべてを自動で行っています。工場内の作物を効率よく育てるために肥沃な土が必要だったってワケ
<img src="https://hackmd.io/_uploads/rkr3my_E1x.png" width="80%">
## サーバー管理プログラムについて
おそらくこっちが本編
### 概要
こんな感じの要件を満たすようにubuntuのservice(systemd)を作成した。
- スケジュールに基づいたサーバーの自動起動、自動終了
- discordにログを投稿
- ワールドデータの自動保存
### スケジュールに基づいたサーバーの自動起動、自動終了
serviceのtimer機能を用いることで定期的実行が可能
サーバー起動用のtimerはこんな感じ
```
[Timer]
OnCalendar=Sun,Tue,Thu,Sat *-*-* 20:30:00
[Install]
WantedBy=timers.target
```
日、火、木、土曜日の20:30に定期的に実行されるようになっている。
サーバーを立ち上げるserviceはこうなっている。
```
[Unit]
Description = minecraft server management daemon
PartOf=minecraft-server-task.service
Wants=minecraft-server-task.service
[Service]
ExecStart=/home/elicxir/mc-utility/start-server.sh
ExecStop=-/bin/kill -SIGINT $MAINPID
Restart=on-failure
User=elicxir
Type=simple
TimeoutSec=infinity
```
ExecStopの
```ExecStop=-/bin/kill -SIGINT $MAINPID```
がミソで、serviceを止めた際にminecraftのサーバーを停止するようにしている。(-SIGINTはワールドの状態をセーブしてから終了する正規の終了処理を行えるらしい。)
```Restart=on-failure```
によってサーバーが異常終了した際には再起動するようになっている
#### サーバー本体とスケジュール管理システム
サーバーとスケジュール管理システムが一体になっているとサーバーが異常終了した際や再起動した際にスケジュール管理システムまで巻き込まれてしまう。それを避けるためにサーバー本体とスケジュール管理システムを分けて並行で実行するようにしている。

### discordにログを投稿
こういう感じでログイン時などにdiscordにメッセージを飛ばす機能を作成。
メッセージの投稿にはwebhookを利用。
<img src="https://hackmd.io/_uploads/SyxITxuNJx.png" width="40%">
内容自体はminecraftのログを解析してwebhookのpostを生成するだけ。
例としてサーバーから退出する時にはこんな感じのログになるので
```
[11Dec2024 00:11:36.680] [Server thread/INFO] [net.minecraft.server.MinecraftServer/]: Rust_Elicxir left the game
```
時刻の部分をうまく吸収できるように正規表現を書いて...
(絶対もっと適切な書き方があるはず)
(mod導入時だと複数の表記があるのでそれらを吸収するのがめんどい)
```
forge_primary_prefix = "^\[[0-9a-zA-Z]{9} [0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}] \[Server thread/INFO] \[net\.minecraft\.server\.dedicated\.DedicatedServer/]:"
forge_secondary_prefix = "^\[[0-9a-zA-Z]{9} [0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}] \[Server thread/INFO] \[net\.minecraft\.server\.MinecraftServer/]:"
default_prefix = "^\[[0-9]{2}:[0-9]{2}:[0-9]{2}] \[Server thread/INFO]:"
prefix_wildcard_without_brackets = f"{forge_primary_prefix}|{forge_secondary_prefix}|{default_prefix}"
```
ログの差分を取得した際にこんな感じで探してあげればok
```
left_match = re.findall(f"({prefix_wildcard_without_brackets}) (.*) left the game", text)
```
### ワールドデータの自動保存
systemdのtimerを利用することで定期的に保存プログラムを走らせることができる。
pythonだとshutilを用いて
``` python
shutil.copytree(savedata_dir, temp)
shutil.make_archive(savename, 'zip', temp)
shutil.rmtree(temp)
```
こんな感じでディレクトリのコピー、圧縮、一時ファイルの削除を行うことができる。
保存をするだけだとどんどん容量を圧迫してしまうので...最新のもの3つを残すようにしている。
~~(誰ですかバックアップを生成しまくってサーバーの容量を使い果たしたのは?)~~
こんな感じで管理しやすいようにファイル名を整えてあげる
```
minecraft_steampunk_2024-12-12-12-00-46
```
*(~~minecraftのlogの時刻フォーマットが[11Dec2024 00:11:36.680]みたいになってるのプログラムから扱うときにだるいからやめてほしい。~~)*
最新のもの3つを残して削除する処理はこんな感じ
```
def clean_up():
target=backup_dir+"/"+savedata_prefix+r'(\d{4})-(\d{2})-(\d{2})-(\d{2})-(\d{2})-(\d{2}).zip'
result = re.match(target, savename)
files = glob.glob(backup_dir+"/*")
fl=[]
for f in files:
result = re.match(target, f)
if result:
fl.append(f)
fl=sorted(fl, key=lambda x: os.stat(x).st_mtime)
fl_erase=fl[:-3]
for f in fl_erase:
os.remove(f)
```
1. prefix("minecraft_steampunk_"の部分)が一致するものを抽出
2. os.stat(x).st_mtimeでファイルの更新日時を取得
3. 更新日時順にソートして最新3つを残して残りは削除
### 感想
minecraftのマルチプレイをきっかけにubuntuのサーバーを触ってみたけど意外と楽しい。
service周りは最善の実装なのかよく分からん。知見ある人改善案あったら教えて~~~。