LuckGameRTA2023の振り返りという名の備忘録(サーバー等)

こんにちは、扶桑です。
先週末行われていたLuckGameRTAにてボランティア(非運営)をしていました。期間を空けながらも1年くらい取り組んでいましたがようやく終わったので、躓いたところや反省を記録に起こしておきます。
なお、全然こうした作業をしてない初心者として詰まった部分の記録です。しょうもないことで躓いてるのも多い。

今回はレイアウトのプログラム部分(NodeCG)の作成とサーバー管理、X(旧Twitter)のクライアントの立ち上げと設定管理などを行いました。

レイアウトについて

レイアウトの設計

レイアウトのpsdデータは運営から渡されたので、それをベースに文字情報を動かせるように組み込みます。
とはいえ、まず何を作ればいいかがわからないと作りようもないのでもらったベースのレイアウトデータを見ながら、仕様を決めました。

今回のレイアウトで必要になる機能は以下のようになりました。

  • RTAするゲームの情報表示(タイトル、カテゴリ、持ち時間)

  • プレイヤー名とSNSアカウントの表示(SNSはTwitch、X、ニコニコ、Youtubeの4つを順番に表示できること)

  • 解説者の名前表示(左枠)

  • 司会者の名前表示(左上)

  • 掲示板機能(右下、Dashboardからテキストを入力しレイアウトに反映させられること)

本当ならここから実装に使用するフレームワークや言語を選定するところですが、予定の納期がまあまあ短かったので、以前試しに作ったプレーンなJavascriptとHTML/CSSのレイアウトを拡張して作成することにしました。
プログラミングを仕事で触ることもないし、書いてる経験なんて普段の配信レイアウト以外にほぼないので

本当ならサクッとSpeedcontrolを使ってやってしまいたいところなのですが、イベントの特性上、
・複数タイトルが同時運用される
・どのゲームがどの枠を利用するかが予定しにくい
・バックアップが大量に入る可能性がある
このあたりでSpeedcontrolをベースに作ってもそれほど楽にならなそうな感じがしてきたため、全部書くことにしました。

コード自体はGithubにほぼ全部上げているのでそちらを御覧ください。
はっきり言ってクソですが、無事動いたので大丈夫です。

振り返り

あとから考えると、かなり効率悪いところが死ぬほどいっぱいあります。
ほぼ同じ挙動をほとんど使い回せてないのが痛い。

本当はReplicantの更新はExtensionに集約させ、Dashboardからmessageを飛ばしてExtensionが感知したら更新の実装にすべきですが、作っていた当時はそれほど気が回らなかったので、Dashboardから直接Replicantを更新しています。ちゃんとExtentionに飛ばすようにすればGraphicsのコードをもっとシンプルにできたと思います。

操作周りについては、どのゲームがどの枠に入っても最悪対応できるようにIDリストを作ってテレビのチャンネル切り替えの要領で変更しています。

ボランティアに公開していた説明用の図解
説明図2:実務

なお、更新をかける度にGoogleスプレッドシートに置いてあるデータを取ってきて反映していますが、これが毎回データを取ってくるようにしてしまっているので実行速度が遅く、かつ不要な通信を何度も行うようになっています。ちゃんとサーバー側にデータを持たせるのが良いでしょう。

Graphicには文字列を自動的に調整する仕組みを入れていますが、結局これはどういうロジックを組んであげるのがいいのか結論が出ないまま実装しました。(graphic/main.js)

function FontSizeAdjust(object, primarysize, coefficient){
	var canvas = document.createElement('canvas');
	var context = canvas.getContext('2d');
	context.font = 36 * primarysize  + "px";
	var metrics = context.measureText(object.innerText);
	var width = metrics.width;
	if(coefficient / width < primarysize){
		object.style.fontSize =  coefficient / width + "em";
	}else{
		object.style.fontSize =  primarysize + "em";
	}
}

ここでは、canvasを用いて文字列の横幅を判定し、幅が大きければ縮小をかけるようにしています。
動作上これで問題なかったですが、これでよかった理由をあまりちゃんと理解しないままできちゃったので要検討。今見ると調整後に確認が入ってないから本当は再帰的な処理をさせないといけないような気がします。しなくていいにしてもその理由がわかってない(アホ)
すごい悩んでいた覚えはあるのですが、このロジックを組んだのがいかんせん1年近く前なので記憶にないですね…
(参考)
2行以上の長いタイトルでも1行に収まるように文字サイズをJavaScriptで調整する - Pixelog
枠のサイズに応じて中のフォントサイズを自動調整する方法 - bravesoft blog
[Javascript] 任意の文字列で、レンダリングに必要な横幅を計算する - YoheiM .NET

音声枠の切り替えについては、psdデータをもらっていたので画像を5種類作ってしまい、背景画像を差し替えることで対応しました。もともとOBSを触らせるんだしそっちで対応でも別にいいかと思って全部投げようとしましたが、実装してほしいとの要望があったので直前でさっと実装しました。

心残りがある点としては、掲示板のシステム実装です。
これは明確に未完成の状態で投げてしまっており、テキストボックスから打ち込む形式になってしまっています。本当ならtextareaで解決するべきでしたが、これがどうやって値受け取ってnodecg.replicantに渡せばいいのかが最後まで理解できなかったので諦めました。テキストボックスで受け取る限り問題なく機能したので、運営にはそのままテキストボックスで調整時にhtmlタグを直打ちするごり押し運用でお願いしました。なお使われなかった模様

運営用に作成したマニュアルの一部

サーバー構築について

コードの段階でかなりぐちゃぐちゃなところも多いのですが、最後に運営やボランティアが操作できるようサーバーの公開作業を行います。
無難にAWSのEC2を使ってサーバーを構築します。
昔ちょっとPythonの関係で触っていた以来で作業方法をほぼ忘れていますし、ほかの人が触ることを想定して組んだことがないのでいろいろ試行錯誤しながら組み立てました。

自分で触るならCUIでいいんですが、ほかの人が触ることも考えてインスタンスに余裕を持たせ、GUI環境をセットアップしました。
今回はインスタンスタイプをt3.medium、AMIにUbuntu Serverを選択し、後からUbuntu Desktopをインストールして対応しました。

(参考)
AWS EC2でデスクトップ環境をつくる ~ Ubuntu Server 18.04 LTS GNOME編~ #AWS - Qiita
EC2上でUbuntu 22.04のデスクトップ環境を作ってi3wmに切り替える – TechHarmony (usize-tech.com)

デスクトップ環境を入れたらNodeCGをインストールして…となるわけですが、そのままだとNode.jsのバージョンが要件を満たさずインストールできないので、最新のバージョンを入れます。
参考:ubuntu 22.04LTSで最新のnodejsぶち込む方法 #Node.js - Qiita
NodeCGをインストールし、作ったレイアウトを入れると環境は完成。

あとは公開するだけ。AWSは初期設定だと起動するたびにグローバルIPアドレスが変わってしまって面倒なので、Elastic IPを取得し、関連付けしてあげます。ついでにドメインをRoute 53で取得しました。

次にSSL接続を行うために証明書を発行してもらいサーバーに配置します。
証明書はおなじみLet’s Encryptで発行。certbotをインストールし、standaloneオプションを指定しつつコマンド実行して証明書を配置して完了です。
参考:NodeCGを活用した配信レイアウトができるまで - 不思議RTAフェス2編 (zenn.dev)

で、この後URLで接続できるようにするわけですが、configの編集でさんざん躓いたのいでメモしておきます。
AWSで動かす場合、設定を以下のように記述します。
(cfg/nodecg.json)
※これはNodeCG本体のフォルダにあります(レイアウトのbundle外)

{
  "host": "プライベートIPアドレス",
  "port": 443,
  "baseURL":"自分で設定したURL"
  "ssl": {
    "enabled": true,
    "keyPath": "/etc/letsencrypt/live/example.com/privkey.pem",
    "certificatePath": "/etc/letsencrypt/live/example.com/fullchain.pem"
  },
  "login":{
    "enabled":true,
    "sessionSecret": "SuperSecret",
    "discord":{
        "enabled":true,
        "clientID": "YourClientID",
        "clientSecret": "YourClientSecret" ,
        "scope":"identify",
        "allowedUserIDs":["1111111111","22222222222"]
        }
    }
}

hostをプライベートIPアドレスにしないと起動できないので忘れずに。
参考:EC2でNode.jsからHTTPサーバ起動したときにEADDRNOTAVAILエラーが出た話 - iq29 diary (hatenablog.com)

今回は一応セキュリティ面を考慮してローカルでのパスワード認証とDiscordでのログインを付けました。(このコンフィグの記載ではローカルパスワードの認証の記載は飛ばしています)
なお、baseURLにURLを入れてあげる必要があることを理解するのが遅かったのでDiscordログインの実装は2日目からだった模様。

ちなみにBaseURLが書いてなくてもパブリックIPアドレス(およびそこに紐づくURL)を叩けばダッシュボードにアクセスできますが、Discord認証ができずに躓きます。
具体的にはBaseURLを設定していないと、リダイレクトURLはhostのローカルIPアドレスを指定しないとつながらないのに認証後に移動できなくなって詰みます。
(ちゃんと図解書いておけばリバースプロキシの設定の必要があるのに気づくのにこんなにかからなかった気もしますが…)

認証はサーバーのRoleIDを指定したほうが権限振り漏れがなくなるのでおすすめですが、ロールIDをコピーする権限やbotを入れる権限を持っていなかったのでユーザーIDで指定しました。
参考:Security & Authentication | NodeCG

ここまでやって公開できて、あとは無事に動いたのでOKです。
大体2日で2ドル程度の費用がかかりました(ドメイン費用別)

Xクライアントについて

クライアントの設定

これについては導入予定はなかったのですが、Xの仕様変更により投入しました。LSSでRiJのfork版を使っていたのでfork版作成者のping値さんにもいろいろ伺いながら設定しました。

これについてはpingさんのドキュメントと元プロジェクトのReadmeを見ながらやればそれほど難しくない…というか面倒になってnode.js v16をインストールしたローカルのPCを用意していろいろごまかしました。v17以降だと結構面倒そうです。
XのRead&WriteのPermissionとアクセストークンを取るほうが難しいくらいで、あとはPackageを無事インストールできるようにエラーメッセージとにらめっこしながら取り組むだけでした。この時期とんでもなく忙しかったのであんまり記憶がないだけかもしれません。
公開方法についてもFork版のドキュメントを見て問題は解決しました。

あとはコンフィグを適宜修正してマニュアルを作成しました。
途中ツイートはしない方針で組んでたので、最初からテンプレートに表示しない仕様にしておくべきでした。しないことはしない、と明示しておかないといけなかったです。
あと{name}に敬称まで入ってるのに全然気づかなかったので中身をちゃんと確認しておくべきでした。

おわりに(感想)

ほとんどどころか全く触ったことのない分野に途中からガンガン拡大したのでめちゃくちゃ大変でした。
準備自体は結構前にやっているものがあったりした関係で、途中期間が開きすぎてて作業の記憶がなくなっていることも多く、途中自分でちゃんとメモしてなかったら本当に危なかったです。メモ大事。

イベント参加者の方々はもちろん、Tweetdeck以降のXの管理対応について教えていただいたping値さんやufoさん、レイアウトのプログラム設計の壁打ちに付き合ってくれたmokomichiUSAさん、作業場所として使っていたJapaneseRestreamの交流所VCで作成途中の愚痴や叫びを聞いて頂いたJapaneseRestreamボランティア経験者の皆様、そしていろいろ経験する場所を用意して頂いたLuckGameRTAの運営陣には改めて感謝いたします。

加えて、こんな雑記を読んでいただいた読者の皆様に感謝します。ありがとうございました。
また思い出せることがあれば追記します。

この記事が気に入ったらサポートをしてみませんか?