Linux」カテゴリーアーカイブ

factorio headlessでSteamに紐づいたアカウントのユーザ名を変更してしまいユーザデータが引き継げなくなった問題の対処

If you change your name your save game will see your new name as a new character. You can use a console command to swap your character back in-game:
/swap-players old-username new-username

https://forums.factorio.com/viewtopic.php?t=16811

ということでコンソール(全角/半角キー)からコマンドを入力するものの、「Admin権限が無いのでコマンドが実行できないよ」と怒られる。headlessでホスト以外にadminを付与する方法を調べた。

factorio-current.log が出力されているディレクトリに server-adminlist.json を新規作成して、admin権限を与えるユーザ名をjson形式で列挙する。その後、factorioを再起動すればOK。(systemctlはconohaサーバのセッティングで利用しています)

# pwd
/opt/factorio/factorio

# ll
drwxr-xr-x 8 factorio factorio 4096 Oct 17 19:03 ./
drwxr-xr-x 5 factorio factorio 4096 Oct 15 15:07 ../
-rw-rw-r-- 1 factorio factorio 1527 Oct 15 15:05 achievements.dat
drwxr-xr-x 3 factorio factorio 4096 Oct 15 15:05 bin/
drwxrwxr-x 2 factorio factorio 4096 Oct 15 15:05 config/
-rw-r--r-- 1 factorio factorio 1001 Jan 7 2021 config-path.cfg
-rw-r--r-- 1 factorio factorio 1479 Oct 17 19:03 console-log
-rw-r--r-- 1 factorio factorio 1479 Oct 17 19:03 console-log.prev
drwxr-xr-x 4 factorio factorio 4096 Oct 17 18:55 data/
-rw-r--r-- 1 factorio factorio 5015 Oct 17 19:03 factorio-current.log
-rw-r--r-- 1 factorio factorio 6207 Oct 17 19:02 factorio-previous.log
-rw-r----- 1 factorio factorio 0 Oct 17 19:02 .lock
drwxrwxr-x 2 factorio factorio 4096 Oct 17 19:02 mods/
-rw-rw-r-- 1 factorio factorio 9079 Oct 17 19:03 player-data.json
drwxr-xr-x 2 factorio factorio 4096 Oct 17 16:39 saves/
drwxrwxr-x 3 factorio factorio 4096 Oct 17 19:03 temp/

# vi server-adminlist.json
# cat server-adminlist.json
[ "username1", "username2" ]
# systemctl stop factorio.service
# systemctl start factorio.service

factorioをconohaで動かしてみた

conoha(www.conoha.jp)というVPSサービスが安くて良さそうなので使ってみた。

これまで、AWSでEC2のt2.microを利用してfactorio headlessを立ち上げていたが、突然ゲーム内のスピードが落ちてカクカクになったのに、topコマンドの結果を見るとCPU使用率は低いまま。原因を調べた結果、tシリーズはCPU creditというものが設定されていて、それを使い切ってしまうと、CPUの能力を十全に活用できずキャップがかかる仕様と判明。そもそも、本来、t2.microはCPUをあまり利用しないサービスが前提となっているものの、起動など一時的にCPUを使用するシーンのみ、お情けとして規定された性能分だけCPU性能をオマケしてもらっている、という仕様らしい。CPUをガンガン常用するようなゲームサーバには適していないことが判明。

じゃあ、適したAWSのインスタンスタイプがどれかなと調べているとき、factorioがプリインストールされているVPSを格安で貸し出してくれる本サービスを発見し利用に至る。注意点は、サーバ停止時も利用料がかかってしまうこと。どうせ停止中でもお金がかかるなら起動しっぱなし(factorio headlessは誰もプレイしていないときはゲーム内時間がSTOPする仕様)とするか、毎回、セーブデータをローカルなどにバックアップしてサーバ自体を削除/新規するかの2択となる。

ちなみに、FAQには詐欺(?)のような書き方で「停止していても料金はかかる」と書いてある。

停止時の料金はありますか?(ConoHa VPS・ConoHa for Windows Server・ConoHa for GAME)
ConoHa VPS・ConoHa for Windows Server・ConoHa for GAMEについて、停止(シャットダウン)いただいた場合でも料金が変動することはございません。サーバーの稼動、停止の状態にかかわらず、ご契約数で料金が発生いたします。

https://support.conoha.jp/common/faq/payment-q/

セキュリティグループはプリセットから下記を適用する。Webコンソールは使いにくいし、セーブデータのアップロードやダウンロードができないので、SSH portを開放してやり、Teratermなど手に馴染んだクライアントソフトから接続するのが吉。

  1. IPv4v6-SSH
  2. IPv4v6-Factorio

セーブデータは下記に格納されている。コマンドからマップのカスタマイズをするのは面倒なので、ローカルのfactorioで作成したセーブデータを同名でアップロードしてやるのが吉。

  • アップロード先:/opt/factorio/savedata/factorio_save.zip
  • アップロード元(Windows Steam版):%appdata%\Factorio\saves

conohaのすごいところは、factorioがプリインストールされているだけではなく、serviceまで作り込まれているところ。enableになっているので、サーバを起動した時点でfactorioが起動している。おなじみのsystemctlからサービスを起動/停止しよう。なお、ポートはデフォルト(34197)となっている。先程設定したセキュリティグループ(IPv4v6-Factorio)も34197が許可されている。

  1. systemctl status factorio.service
  2. systemctl stop factorio.service
  3. systemctl start factorio.service

statusを確認してみた。

# systemctl status factorio.service
● factorio.service - Factorio Server
     Loaded: loaded (/etc/systemd/system/factorio.service; enabled; vendor preset: enabled)
     Active: active (running) since Tue 2023-10-10 09:30:36 JST; 2h 8min ago
   Main PID: 138266 (sh)
      Tasks: 12 (limit: 1065)
     Memory: 651.7M
     CGroup: /system.slice/factorio.service
             tq138266 /bin/sh -c /usr/bin/screen -DmS factorio /opt/factorio/factorio/bin/x64/factorio --start-server /opt/factorio/save>
             tq138267 /usr/bin/SCREEN -DmS factorio /opt/factorio/factorio/bin/x64/factorio --start-server /opt/factorio/savedata/factor>
             mq138276 /opt/factorio/factorio/bin/x64/factorio --start-server /opt/factorio/savedata/factorio_save --console-log /opt/fac>

Oct 10 09:30:36 vm-1c3075e2-0b systemd[1]: Started Factorio Server.

config.iniはデフォルトなので、必要に応じて修正する。デフォルトだと、10分間隔にオートセーブでフリーズがかかってうざいので、autosave-intervalは長めにしておくことを推奨。この方法だと、何故か設定が適用されない不具合が発生。解消方法は後述。

# pwd
/opt/factorio/factorio/config

# diff config.ini.default config.ini
21c21
< ; autosave-interval=5
---
> autosave-interval=20
23c23
< ; autosave-slots=3
---
> autosave-slots=5

上記の方法だと何故か設定が反映されない。別の方法を模索した結果、下記のjsonで設定してやるとうまくいくことが判明。exampleをコピーして設定ファイルを新規作成。仲間内でのゲームなので、public/lanをいずれもfalseにして、autosave_intervalを60分に変更。その後、この設定ファイルを読み込むよう、起動オプションに追加してやる。

  • /opt/factorio/factorio/data/server-settings.example.json
  • /opt/factorio/factorio/data/server-settings.json
# diff server-settings.example.json server-settings.json
13,14c13,14
<     "public": true,
<     "lan": true
---
>     "public": false,
>     "lan": false
48c48
<   "autosave_interval": 10,
---
>   "autosave_interval": 60,

serviceファイルの起動オプションに「–server-settings /opt/factorio/factorio/data/server-settings.json’」を付与する。systemctl daemon-reloadでserviceの再読み込みを忘れずに。

# cat /etc/systemd/system/factorio.service
[Unit]
Description=Factorio Server
After=network.target nss-lookup.target

[Service]
Type=simple
User=factorio
Group=factorio
WorkingDirectory=/opt/factorio
ExecStart=/bin/sh -c '/usr/bin/screen -DmS factorio /opt/factorio/factorio/bin/x64/factorio --start-server /opt/factorio/savedata/factorio_save --server-settings /opt/factorio/factorio/data/server-settings.json'
ExecStop=/usr/bin/screen -p 0 -S factorio -X eval 'stuff "/server-save\015"'
ExecStop=/bin/sleep 5
ExecStop=/usr/bin/screen -p 0 -S factorio -X eval 'stuff ^C'
ExecStop=/bin/sleep 5
Restart=always

[Install]
WantedBy=multi-user.target

# systemctl daemon-reload

WSLインストール時のエラー(0x80370102)

WSLをインストール(wsl –install)後に再起動するとコマンドプロンプトが自動で起動しインストールが始まるものの、下記のエラーが発生した。調べてみると、Intel CPUで言うところのVirtualization TechnologyがOFFになっていたことが原因だった。AMD CPUでは「AMD Virtualization (AMD-V™)」と呼ぶらしく、ASUSマザーボードのUEFI BIOSでの設定項目は「SVM MODE」ということだった。

WslRegisterDistribution failed with error: 0x80370102

Windowsコマンドプロンプト

最近のPCでは、再起動後にASUSのロゴが表示されない(早すぎてFxキーでUEFI BIOS画面に入れない?)ようで、Shiftキーを押しながら、Windowsの左下スタートメニューの再起動を実行することでUEFI BIOS画面に入ることが出来る。

更に、HDMIとDPを両方使用して2画面構成にしている場合、DP側のみにUEFI BIOS画面が表示されるので注意。私の場合、DP側をサブディスプレイに出力させ、普段はディスプレイをOFFにしていたので数分ハマった。

UEFI BIOS画面に入ったら、右下のAdvanced Mode > Advanced > CPU Configuration > SVM Mode > Enable > 右下EzMode > Save & Exitで再起動すればOK。

再起動後は、左下スタートメニュー > 最近追加されたもの > Ubuntuを選択することでインストールを再開できる。新規作成するユーザ名を聞かれパスワードも合わせて設定することで設定完了する。

waitコマンドとLinux/Unixの仕様メモ

バックグラウンドプロセス2個以上をwaitして実行結果を取得する際のメモです。Linux(RHEL7、CentOS7)で確認済みですが、Unixでも同じことが言えるはずです。

まずは間違っている例です。下記の様に、プロセスIDを取得して、waitする際に2個のプロセスIDを指定してしまうと、誤った結果が得られます。(最後に指定したプロセスIDの結果のみが取得できる。)

$ cat parent.sh
#!/bin/sh

./child.sh $1 &
child1p=$!

./child.sh $2 &
child2p=$!

sleep 3

wait $child1p $child2p
childr=$?

echo "child1p:$child1p, child2p:$child2p, childr:$childr"
$ cat child.sh
#!/bin/sh
sleep 1
exit $1

下記が実行結果です。

$ time ./parent.sh 1 1
child1p:10159, child2p:10160, childr:1

real    0m3.005s
user    0m0.005s
sys     0m0.003s
$ time ./parent.sh 0 0
child1p:10165, child2p:10166, childr:0

real    0m3.005s
user    0m0.004s
sys     0m0.003s
$ time ./parent.sh 0 1
child1p:10171, child2p:10172, childr:1

real    0m3.005s
user    0m0.001s
sys     0m0.006s

うまくいくやん! と、思われ方。要注意! 下記を実行すれば誤りに気付きます。

$ time ./parent.sh 1 0
child1p:10152, child2p:10153, childr:0

real    0m3.006s
user    0m0.004s
sys     0m0.004s

引数pidを指定した場合,waitコマンドは最後の完了を待ったプロセスのコマンドの終了コードで終了します。
http://itdoc.hitachi.co.jp/manuals/3021/3021313320/JPAS0399.HTM

正しい実装は下記。それぞれ個別にwaitする必要がある。

$ cat parent2.sh
#!/bin/sh

./child.sh 0 &
child1p=$!

./child.sh 1 &
child2p=$!

sleep 3

wait $child1p
child1r=$?

wait $child2p
child2r=$?

echo "child1p:$child1p, child2p:$child2p, child1r:$child1r, child2r:$child2r"

ん? と思われた方。そう、子プロセスが[sleep 1]しているにも関わらず、親プロセスで[sleep 3]しているため、waitにたどり着くまでに子プロセスは終了しています。「終了したプロセスの実行結果なんて取得できるの?」と。

waitコマンドについて詳しく言及した記事はありませんでしたが、waitシステムコールについて調べると、ありました。

終了したが、wait されていない子プロセスは「ゾンビ」になる。後で親プロセスが wait を実行して子プロセスについての情報を取得できるように、カーネルはゾンビプロセスについて最小限の情報 (PID、終了ステータス、 リソース使用状況) を保持する。ゾンビプロセスは、 wait によってシステムから削除されない限り、カーネルのプロセステーブルの 1 エントリーを消費する。このプロセステーブルが 一杯になると、新たにプロセスを作ることができなくなる。親プロセスが終了すると、その親プロセスの「ゾンビ」の子プロセスは (もしあれば) init(1) の養子となる。 init(1) は wait を自動的に実行し、ゾンビを削除する。
https://linuxjm.osdn.jp/html/LDP_man-pages/man2/wait.2.html

「養子」という表現、かなり好きです。(そこではない) つまり、子プロセスの実行結果は、親プロセスが終了するまでは間違いなく保持されるようです。そのため、上記コードは問題なしです。

$ time ./parent2.sh 1 0
child1p:10185, child2p:10186, child1r:0, child2r:1

real    0m3.006s
user    0m0.004s
sys     0m0.004s

位置100で不正な入力シーケンス(iconv)

「位置xxxで不正な入力シーケンス」は、いわゆるWindowsの機種依存文字である丸付き数字等をiconvでutf8→shift_jisに変換しようとしたときに発生するエラー。丸付き数字は、utf8では規定されているが、厳格なshift_jisには存在しないので、対応する文字無しということでエラーになる。(Windowsにおけるshift_jisは、正確にはshift_jisではなく、暗黙的に拡張文字(丸付き数字等)を含んだもの。)拡張文字も含んだcp932を変換後の文字コードとして指定してやれば解消する。

下記は、Linux上の日本語名ファイルを、zip等に纏めてftpでWindowsに送ったときに文字化けさせない対応のサンプルスクリプト。(utf8→cp932に変換している。)

mv "$filename" "`echo $filename | iconv -f utf8 -t cp932`"

意地でも正規表現を使わずに拡張子とファイル名を分割する

良く使うのに毎回調べている気がする正規表現をあえて意地でも使用せずに、拡張子(.tar.gz)とファイル名を分割する。ファイルのバックアップをアンダースコア+日付でcpして取得する場合が良くあるが、そういったときのアンダースコア分割にも応用できる。特にアンダースコアはファイル名自体にも使用されることがあるので、revで逆さにしてから変換する。(セパレータが何個存在するか分からないので、逆から処理するとうまくいく)

echo test.test.tar.gz | rev | cut -d '.' -f 3- | rev

逆にして、先頭からセパレータを探索、3個目以降を取得、再度逆にする。言われてみるとシンプルだけれど、なかなか出来なかった発想。