ApexModの常時稼働サーバーを作る

2022年11月6日

Flowstateのお陰で最近成長しているApex Legends用Mod、
R5Reloadedで常時稼働サーバーのKSKT OitaJPN Serverを作った話です。
Linux起動もモニタリングもたどり着くまで苦労したので、メモとしても残しておきます。
そしてこれからR5Reloadedサーバーを作りたい人のためにも。

そもそもR5Reloadedってなんなの

Apex LegendsのModで、Season 3のファイルを使って作られてます。
リリースされたときにそこそこ話題になり、そこからそこまで話題になることはありませんでしたが、
Flowstate Aim Trainerによって再び話題になりました。

とりあえずLinuxで起動してみる

環境

  • Ubuntu Server 20.04.5 LTS
  • Intel Core i5-4570(4Core) 3.6GHz
  • Intel HD Graphics
  • Memory 16GB
  • ウィンドウ環境なしのCUI

そうです、このサイトと一緒のサーバーです。
内蔵グラフィックで問題ありませんが、あまりに古いCPUだとSSE3がなくて起動できないかも。

その前に

作る前にメインPCなどで普通に公開サーバーをホストできるか確認してください。
UDPで37015-37020を開放しておく必要があります。PublicにしてBroadcasting!がサーバーブラウザーで出てれば問題ないと思います。ポートフォワーディングを複数のIPにはできないので、
作り始める前にメインPCからサーバーPCにポートフォワーディング先を変更してください。

起動する

1.Dedicatedサーバーをダウンロードする

最初に公式Discordサーバー( https://discord.com/invite/jqMkUdXrBr )の#updatesから
最新のDedicatedサーバーをダウンロードしておく必要があります。
ダウンロードが終わったらサーバーに展開します。3GBほどの空き領域が必要です。
Sambaなどでファイルをドロップする際は、実際にDedicatedサーバーを実行するユーザーが読み取り,書き込み,実行権限を持つようにしてください。chownで所有者変えるのが一番手っ取り早いし確実です。

2.とりあえずWineで動かす

Wineで一回動かします。インストールしてないなら最新のwineをインストールします。
( https://wiki.winehq.org/Ubuntu から手順を確認)
インストールしたら、Dedicatedサーバーのディレクトリに移動して、
wine r5apex_ds.exe を実行して動くかどうか確認します。(rootで動かさないでください!)

3.設定を変える

platform/cfg に移動して、autoexec_server.cfg を編集します。
hostnameの部分をアンコメントしてサーバー名を書きます。
sv_requireOriginTokenは特別な理由がない限り1にしておくべきです。
その下に以下をコピペします。launchplaylistとmapはお好みで設定してください。
hostname,hostdescには.と-以外の記号を使えないので注意してください。
(それ以外を使うとサーバーブラウザーに乗りません)

hostdesc                     "Server description here"
map                          "mp_rr_desertlands_64k_x_64k"      // Map
sv_pylonVisibility           2        // Server Browser Visibility. (0=Disable,1=Hidden,2=Public)
sv_cheats 0
launchplaylist custom_tdm

これでLinuxでR5Reloaded用公開サーバーを開けるようになりました。
Wineのエラーログがやかましい場合は実行する時にWINEDEBUG=fixme-all を前に付けて
WINEDEBUG=fixme-all wine r5apex_ds.exe といった感じで起動すると隠すことができます。

これでLinuxでR5Reloadedサーバーを建てることができました。
しかし、やはりModなのでクラッシュすることが結構あります。Flowstateなら尚更。
なのでクラッシュした際には自動で再起動させるようにしないといけません。
てことで生きてるかどうかのモニターをする必要があります。

モニタリングする

モニタリングと言っても、R5Reloadedサーバーのクラッシュチェックはそんなに簡単ではありません。
Titanfall系統のクラッシュには主に2種類あり、Engine crashとScript crashに分かれます。
Engine crashの場合はプロセスが終了するので、純粋にプロセスが生きてるかどうかで判断できますが、
主に発生するScript crash(厳密には重大なScript error)はプロセスが終了せず内部的にはタイトル画面に戻るので、
プロセスが生きてるかどうかでは判断できません。

ということで、具体的にサーバーの状態を見てやる必要があります。
その時に使えるのがサーバーを遠隔操作できるrconです。
statusという今のサーバー状態を返してくれるコマンドがあるので、
rconを使用してstatusコマンドを送信し、その戻り値にサーバー名が入っているかどうかで判断することにしました。

サーバーが動いてる状態でstatusコマンドを使用すると、このように返ってくる

rconを設定する

最初にサーバー側でrconの設定をしてやる必要があります。
platform\cfg\rcon_server.cfgを以下のように編集します。

rcon_password             "rconpasswordhere"
sv_rcon_whitelist_address "127.0.0.1"

しかし、R5Reloadedのrconは改変が入っているので、pythonのライブラリーなどは使えません。
R5Reloadedのbinフォルダー内に入っているnetcon32を使います。Wineで動きます。
なので、まずテキストファイルを一個用意します。cc_cmd.txtとしました。

127.0.0.1 37015
PASS rconpasswordhere
status

次に、cat cc_cmd.txt | wine netcon32.exe として実行すると、問題なく接続→認証→status実行と進むはずです。
若干不安定なので実際の環境ではstatusを2回打っています。
では、今度はその戻り値をどうやって処理するかです。安易に考えられるのはリダイレクトです。
しかし、cat cc_cmd.txt | wine netcon32.exe > output.txt とやってもコマンドの戻り値を吐き出してくれません。

R5Reloaded TCP net console [Version 2.0.0.1]Mod version
Enter <IP> <PORT>: Connected to: [127.0.0.1]:37015

なぜこうなるのかは不明です。shにコマンドを書いてリダイレクトしてもダメだったので、netconを改造します。

netconを改造する

https://github.com/Mauler125/r5sdk からソースコードをダウンロードして、platforms内に配置します。
r5dev\netconsole\netconsole.cpp がnetconのコードです。
その後は適当に調べたファイル出力のコードを引っ張ってきます。#include <fstream> でfstreamをincludeして、
430行目あたりのbreakの前に以下を書きます。

		ofstream logfile;
		logfile.open("output.txt", std::ios::app);
		logfile << svOut;
		logfile.close();

これでビルドすると、サーバーからの戻り値がoutput.txtに書き込まれます。
その後はexeとpdbをサーバーに持っていって、動くかどうか確認します。
32bit用のnetcon32と64bitのnetcon64がありますが、Wineで動かすことを考えて32bitを持っていきます。

シェルスクリプトを書く

そしたらシェルスクリプトの時間です。KSKTはdockerやデーモン化を行わず、screenを使用してます。超シンプル。
まずは実行するとクラッシュしてるかどうかを判断してクラッシュしてる場合に再起動するスクリプトです。

#!/bin/sh
cd apexdirhere
echo "Checking crash..."
rm -rf output.txt
touch output.txt
cat cc_cmd.txt | timeout 5 wine netcon32.exe -nocolor
if grep -q 'YourserverName' output.txt; then
  echo "server is alive!!!11!11!!!!!!!"
else
  echo "double checking..."
  cat cc_cmd.txt | timeout 5 wine netcon32.exe -nocolor
  if grep -q 'YourserverName' output.txt; then
    echo "double check passed"
  else
    echo "omg server is dead!!!11!11!!!!!!!"
    echo "server restarting..."
    r5pid=-1
    r5pid=$(pgrep r5apex_ds.exe)
    if [$r5pid = ""]; then
      echo "r5 ha mou shindeiru"
    else
      kill $r5pid
      echo "r5 has killed by checkcrash"
    fi
    screen -UAmdS r5r_dedi_server sh startdedi.sh
  fi
fi

echoの文章が全てやかましいのは置いておいて、apexdirhereはdedi鯖のあるディレクトリで書き換えて、
YourserverNameはサーバー名に含まれている何かしらの文字列で置き換えてください。
サーバーの名前を書くのが良いです。自分はKSKTにしてます。
サーバーが閉じている時、statusが何も返さない性質を使用して文字列が含まれているかどうかで判断します。

プロセスが死んでればそもそも接続できないので、戻り値に文字列が含まれておらず死んでると判断されます。
前のoutput.txtを削除して、cc_cmd.txtをパイプしてnetcon32.exeを色無しで起動します。猶予時間は5秒です。
grepでoutput.txtの内容を読み取り、サーバー名が入っていればサーバーが生きてると判断されます。
入っていない場合は一応再度チェックを行い、それでも死んでる場合はプロセスをキルして、サーバーを再起動します。
startdedi.shの内容はすごくシンプルで、fixmeを黙らせる形でdedi鯖を起動する一行のコマンドです。

#!/bin/sh
WINEDEBUG=fixme-all wine r5apex_ds.exe

後は60秒おきにcheckcrash.shを実行する以下のシェルスクリプトと、

#!/bin/sh
while true
do
  echo "waiting 60s..."
  sleep 60
  date
  echo "Checking crash..."
  sh checkcrash.sh
done

サーバー開始用のシェルスクリプトで完成です。(rconがinitされるまで少しかかるので、十秒置いてます)

#!/bin/sh
echo "Launching Dedicated Server..."
screen -UAmdS r5r_dedi_server sh startdedi.sh
sleep 10
echo "Starting Server Monitor..."
screen -UAmdS r5r_dedi_monitor sh monitor.sh
echo "Launched"

これで全部完了!ここまでのファイルは改変されたnetcon32も含めてダウンロードページで配布されています。

おわりに

ということでKSKT OitaJPN Serverはこんな感じに動いてます。
後はご自分でスクリプト改造なり色々やってください。
Pufferpanelとかありますけど、自分は極力複雑化させたくないのでこれからもscreen使います。
現に複雑化しすぎて「とりあえず動いてるからヨシ!」状態になってるのがこのサイトですし。
R5Reloadedのサーバーを立てたい人がどれくらいいるのかは知りませんけど、
チャレンジしてみたい人の助けになれば幸いです。ではでは~~~~~~~~~~~~~~