[go: up one dir, main page]
More Web Proxy on the site http://driver.im/

LinuxでDisplay HDRを有効にしてゲームをプレイする方法

PS5やWindows PCの様にLinuxHDR対応コンテンツを楽しめるのかですが、結論から言えば、一応可能です。 ただ、そんなにお手軽かというとそうではないし、それなりに制約があります。また、自分の目ではちゃんとなっている様に見えますが本当に想定された色が出てるのかというと良く分からんという感じではあります。

という訳で、多少問題はあるかもしれませんが可能は可能なので、その実現方法について紹介します。

LinuxHDRサポート状況

参考: HDR monitor support - ArchWiki

現状、デスクトップ環境レベルでHDRをサポートしているのはKDE Plasmaがwaylandセッションで実験的にサポートしているだけです。 X.orgにおいてはサポートされる見込みは恐らくありません。

GNOMEデスクトップはサポートに向けて頑張っている最中の様です。

DRMレベルでHDR metadataを扱うことは可能です。一般的なuserspaceのクライアントで利用可能なものはありませんが、特定のソフトウェアで対応しているものがあります。

自分が利用しているHyprlandというwaylandのWMは実装が進められている様です。今後に期待したい。(https://github.com/hyprwm/aquamarine/pull/112)

という訳で、現時点でまともにHDRコンテンツを扱う方法は自分が知ってる限りではgamescopeを利用する方法だけです。

gamescopeについて

gamescopeはValveSoftwareがSteamOS向けに開発したWindow Managerです。 gamescopeは単体のGUIアプリを起動するのに特化したWindow Managerでゲームフレームの描画に最適化されています。そのため、通常のデスクトップ環境で起動するよりもゲームフレームの描画がスムーズになります。

gamescopeにはembeddedモードとnestedモードという二種類のモードがあり、通常のデスクトップ環境で起動するとnestedモードになります。Xwaylandのサンドボックスデスクトップを起動して既存のデスクトップ環境と干渉することを避け、任意の解像度とリフレッシュレートを偽装することができます。

embeddedモードではDRM/KMSを利用してXwaylandを起動し、この上でゲームを起動することで余計なフレームコピーを回避して描画を高速化しています。

このembeddedモードでDRM/KMSを利用する場合において、HDRを有効化できます。

Steamをプレイする場合の実行手順

最初にgamescopeのhelpを紹介しておきます。

 gamescope --help                                                                 
[gamescope] [Info]  console: gamescope version  (gcc 14.2.1)
usage: gamescope [options...] -- [command...]

Options:
  --help                         show help message
  -W, --output-width             output width
  -H, --output-height            output height
  -w, --nested-width             game width
  -h, --nested-height            game height
  -r, --nested-refresh           game refresh rate (frames per second)
  -m, --max-scale                maximum scale factor
  -S, --scaler                   upscaler type (auto, integer, fit, fill, stretch)
  -F, --filter                   upscaler filter (linear, nearest, fsr, nis, pixel)
                                     fsr => AMD FidelityFX™ Super Resolution 1.0
                                     nis => NVIDIA Image Scaling v1.0.3
  --sharpness, --fsr-sharpness   upscaler sharpness from 0 (max) to 20 (min)
  --expose-wayland               support wayland clients using xdg-shell
  -s, --mouse-sensitivity        multiply mouse movement by given decimal number
  --backend                      select rendering backend
                                     auto => autodetect (default)
                                     drm => use DRM backend (standalone display session)
                                     sdl => use SDL backend
                                     headless => use headless backend (no window, no DRM output)
                                     wayland => use Wayland backend
  --cursor                       path to default cursor image
  -R, --ready-fd                 notify FD when ready
  --rt                           Use realtime scheduling
  -T, --stats-path               write statistics to path
  -C, --hide-cursor-delay        hide cursor image after delay
  -e, --steam                    enable Steam integration
  --xwayland-count               create N xwayland servers
  --prefer-vk-device             prefer Vulkan device for compositing (ex: 1002:7300)
  --force-orientation            rotate the internal display (left, right, normal, upsidedown)
  --force-windows-fullscreen     force windows inside of gamescope to be the size of the nested display (fullscreen)
  --cursor-scale-height          if specified, sets a base output height to linearly scale the cursor against.
  --hdr-enabled                  enable HDR output (needs Gamescope WSI layer enabled for support from clients)
                                 If this is not set, and there is a HDR client, it will be tonemapped SDR.
  --sdr-gamut-wideness           Set the 'wideness' of the gamut for SDR comment. 0 - 1.
  --hdr-sdr-content-nits         set the luminance of SDR content in nits. Default: 400 nits.
  --hdr-itm-enable               enable SDR->HDR inverse tone mapping. only works for SDR input.
  --hdr-itm-sdr-nits             set the luminance of SDR content in nits used as the input for the inverse tone mapping process.
                                 Default: 100 nits, Max: 1000 nits
  --hdr-itm-target-nits          set the target luminace of the inverse tone mapping process.
                                 Default: 1000 nits, Max: 10000 nits
  --framerate-limit              Set a simple framerate limit. Used as a divisor of the refresh rate, rounds down eg 60 / 59 -> 60fps, 60 / 25 -> 30fps. Default: 0, disabled.
  --mangoapp                     Launch with the mangoapp (mangohud) performance overlay enabled. You should use this instead of using mangohud on the game or gamescope.

Nested mode options:
  -o, --nested-unfocused-refresh game refresh rate when unfocused
  -b, --borderless               make the window borderless
  -f, --fullscreen               make the window fullscreen
  -g, --grab                     grab the keyboard
  --force-grab-cursor            always use relative mouse mode instead of flipping dependent on cursor visibility.
  --display-index                forces gamescope to use a specific display in nested mode.
Embedded mode options:
  -O, --prefer-output            list of connectors in order of preference
  --default-touch-mode           0: hover, 1: left, 2: right, 3: middle, 4: passthrough
  --generate-drm-mode            DRM mode generation algorithm (cvt, fixed)
  --immediate-flips              Enable immediate flips, may result in tearing
  --adaptive-sync                Enable adaptive sync if available (variable rate refresh)

Debug options:
  --disable-layers               disable libliftoff (hardware planes)
  --debug-layers                 debug libliftoff
  --debug-focus                  debug XWM focus
  --synchronous-x11              force X11 connection synchronization
  --debug-hud                    paint HUD with debug info
  --debug-events                 debug X11 events
  --force-composition            disable direct scan-out
  --composite-debug              draw frame markers on alternating corners of the screen when compositing
  --disable-color-management     disable color management
  --disable-xres                 disable XRes for PID lookup
  --hdr-debug-force-support      forces support for HDR, etc even if the display doesn't support it. HDR clients will be outputted as SDR still in that case.
  --hdr-debug-force-output       forces support and output to HDR10 PQ even if the output does not support it (will look very wrong if it doesn't)
  --hdr-debug-heatmap            displays a heatmap-style debug view of HDR luminence across the scene in nits.
Reshade shader options:
  --reshade-effect               sets the name of a reshade shader to use in either /usr/share/gamescope/reshade/Shaders or ~/.local/share/gamescope/reshade/Shaders
  --reshade-technique-idx        sets technique idx to use from the reshade effect

Steam Deck options:
  --mura-map                     Set the mura compensation map to use for the display. Takes in a path to the mura map.

Keyboard shortcuts:
  Super + F                      toggle fullscreen
  Super + N                      toggle nearest neighbour filtering
  Super + U                      toggle FSR upscaling
  Super + Y                      toggle NIS upscaling
  Super + I                      increase FSR sharpness by 1
  Super + O                      decrease FSR sharpness by 1
  Super + S                      take a screenshot
  Super + G                      toggle keyboard grab

オプションの中に--hdr-enabledなどがあることが分かりますが、この辺を利用します。

embeddedモードで起動しなければならないので、まずCTRL+ALT+F2(〜F6)ホットキーを使って、デスクトップ環境が起動していないセッションに切り替えます。どの番号が空いてるかは環境に依りますが、大抵F1かF7でデスクトップが起動していると思うのでF2は空いてるでしょう。

コンソール上で以下のコマンドを使ってsteamを起動します。

ENABLE_GAMESCOPE_WSI=1 ENABLE_HDR_WSI=1 DXVK_HDR=1 gamescope -e --xwayland-count 2 -o "DP-1,*" --hdr-enabled --hdr-itm-enable -- steam -gamepadui

DP-1の部分は自身が接続しているディスプレイのポートに合わせて読み替えてください。

WSI関連の環境変数はgamescopeのHDR対応の有効化とvulkan APIHDR対応の有効化に必要な様です。 DXVK_HDR=1は、ProtonやWineで起動するゲームアプリケーションのHDR対応を有効化するために必要で、VulkanでエミュレートしているDirectXAPIに対してHDR機能の有効化を指示する環境変数です。この環境変数を付けて起動すれば、対応しているゲームならゲーム内の設定でHDRの有効・無効が切り替えられる様になるはずです。

上記のコマンドでsteamが起動すると、この時点でディスプレイのHDRモードが有効になるはずです。

この状態で、steamのゲームを起動し、DXVK_HDR環境変数が正しく渡っていれば、HDR対応のゲームをHDRモードでプレイできるはずです。もしゲーム内まで環境変数が伝播していなければ、steamtinkerlaunchなどの起動ラッパーを使うか、steam上の設定で実行コマンドに環境変数を付与すれば対応できるでしょう。

自分の環境ではサイバーパンク2077とDMC5において、HDRモードでプレイすることができました。

DRMで直接steamを起動していることによる制約

この状態だとディスプレイが1枚しか使えません。ゲームをプレイしつつサブディスプレイでWebブラウザを表示するなどは不可能です。

CTRL+ALT+F1でデスクトップ環境が起動しているセッションに戻ることで通常のデスクトップアプリを操作することはできます。この時、ディスプレイのHDRモードが自動でオフになったりはしないので、やたらビビッドな色で表示されることになりますが、攻略サイトを見たりすることは出来ると思います。

steamを落としてgamescopeプロセスを終了したらディスプレイのHDRモードが解除されるはずです。

HDR動画を試聴する

世の中にそんなにHDR対応の動画コンテンツは無いんですが、mpvとgamescopeを組み合わせることでHDRモードで動画を再生することが出来ます。

mpvのconfigに以下の内容を追加します。

[hdr]
target-colorspace-hint=yes
inverse-tone-mapping=yes
target-prim=bt.2020
target-trc=pq
target-peak=600

大事なのはtarget-colorspace-hintとinverse-tone-mappingです。

設定を追加したら、以下の様にmpvを起動します。

ENABLE_GAMESCOPE_WSI=1 ENABLE_HDR_WSI=1 gamescope -e --xwayland-count 2 -O "DP-1,*" --hdr-enabled -- mpv --profile=hdr <movie file>

これでディスプレイがHDRモードに切り替わった状態でmpvが起動し、tone-mappingによるHDR->SDR変換ではなくHDRの色情報のまま動画が表示できるはずです。

多分、色的にも正しいと思うのですが、そもそも理想的な色状態に確信が無いので、ちょっと何とも言えないところはありますが……。

という訳で、gamescopeを使って頑張れば現状のLinuxでもHDR対応コンテンツを楽しむことは出来ると思います。

この記事が誰かの参考になれば幸いです。

大阪Ruby会議04でRustでRubyGemを書く話をしてきました

8/24に開催された大阪Ruby会議04に参加してました。

自分は地元が関西なのもあって相性が良いのか、大阪で開かれた地域Ruby会議ではよく登壇する機会があって、今回もそうなることができて良かったなと思っています。

今回の大阪Ruby会議はチーフオーガナイザーであるydahさんの趣味が出てる感じがして、めちゃくちゃ楽しいイベントでした。なんというかめっちゃRubyKaigiそのものっぽかった。

実は少し前に実家の母が倒れて入院したりといった事件があったので、お見舞のために奈良と大阪を往復したり、ホテルが1日ごとに変わって荷物がやたら多くなったりと結構バタバタしてしまって体力は消耗したんですが、結果的には無事に楽しむことができて安心しました。

セッション内容について

今回の両キーノートスピーカーである金子さんとはすみさんは、この二人なら信頼感しか無いなと思っていたのですが、期待以上に素晴らしい発表でした。

色々バタバタしてたのもあって金子さんのブログは事前に読めていなかったのですが、エディタとLSP周りは色々調べたことがあって、その延長でConcrete Syntax Tree (Lossless Syntax Tree)とSwiftやC#の関係についても少し触れたことがありました。前日の飲み会でその辺りの触りを聞くことができたので、色々心構えができて、楽しく聞くことができました。

はすみさんのキーノートはMatzスタイルにkakutaniさんが話しているかの様な良い話が組み合わさって、いかにもキーノートって感じで素晴らしかった。良い意味で「役に立たない」話って楽しいですよね、っていうのはRubyKaigiを始めとする技術者コミュニティではしばしば感じられる価値観で、自分もとても好きな考え方の一つです。自分もそういう風にネタを提供できたら良いなあと思いました。

キーノート以外で、今回めっちゃ良いなと思ったのはkoicさんのminifyrbの話です。Rubyをminifyするというのは現時点でそんなに実用性が無いことなんですが、そこからRubyの意味論の話に繋がって、そしてその上で自分がコミッタをやっているRubocopでソースコードを直すという完璧なマッチポンプに繋がっていました。しかもそのおかげでRubocopのエッジケースのバグが見つかって修正に繋がっている。楽しそうだから遊んでみて、結果OSSがちょっと便利になっているというのは本当に良い話だなと思いました。

自分の登壇について

発表資料はこちらです。 Rustで作るtree-sitterパーサーのRubyバインディング - Speaker Deck

作ったもの。 github.com

パーフェクトRubyという本を書いた時にC拡張の話を書いたことがあって、今だとRustでも書けるしやってみたいなあと思っただけというやつですが、色々と勉強になることがありました。

今回、細かいRustの解説をやっている時間がなかったので、Rustの知識がかなり前提になってしまう発表になってしまったのが反省点の一つです。なので、ここで少し補足を書いておきたいと思います。

RustでRubyGemを書くことの特徴の一つとして、メモリモデルの理解が必要になるという点が上げられます。

RustはGCが無い言語ですが、所有権システムとライフタイムによって必要なくなったデータを利用者が余り意識しなくても開放してくれます。また、基本的にデータはスタックに置くという考え方があると思っています。

スタックというのは雑に言えば、関数呼び出しをした時にその関数のために割り当てられる領域です。関数呼び出しから更に関数呼び出しを行うと、領域が積み上げられて終わったら結果を取り出して開放するという動きから積み重ねという意味でスタックと呼びます。

スタックは関数呼び出しが終わったらすぐに開放されます。例えばRubyでメソッドの中で定義したローカル変数はメソッドを抜けたら使えなくなりますが、Rustではもうちょっと広い領域で考える必要があります。

Rustの関数呼び出しの引数として所有権を持った型を受け取る様になっていると、その関数の呼び出しが終わった時に返り値としてその値を返さない限りはスタック開放時に一緒に受け取った引数も開放されて消滅します。所有権を引き渡すということは、基本的にはもう元の箇所で使わないという意志表示であり、関数の返り値になっていないということは関数の呼び出し元でも必要ないという意志表示です。なのでRustは必要なくなったデータを開放します。

そうなると困るデータを扱うために借用という概念を利用します。借用を利用すれば、データの所有権は元のスタックに残したまま、呼び出し先で値を参照したり、参照先のデータを書き換えたりできる様になります。

Rustで普通にコードを書いている時は、借用型やスタックに置いてるデータを意識することでデータの生存期間をコントロールできますが、RubyGemを書く時にいくつかの制約があってコントロールが難しくなります。

一番問題なのは、Rubyからのメソッド呼び出しの結果、いきなりRustの世界に入ってきて(その様に見える)その関数を抜けたらRustが理解しているスタックが消滅することです。

また、RustがアロケートしたデータをRubyオブジェクトとして利用したい時は、そのデータをRust側でヒープに置いてはいけないという制約もあります。これはRustからヒープに置いたデータをRuby側で認識できないので、そうしてしまうとメモリリークに繋がってしまうからです。これはつまり、Rust上でコードを書く時、Rubyのオブジェクトとして扱いたいデータをVec型などのヒープにデータ領域を確保する型に入れてはいけない、ということを意味します。これは結構面倒臭い。

という訳で、基本的にRustでアロケートした構造体などをRubyオブジェクトとして利用するためには、参照を受け取って所有権のある型を返すか、Rubyの管理しているヒープにオブジェクトとして存在していることが分かっているデータの参照を返す必要が出てきます。この辺のメモリの扱い方の関係でRubyGemとして利用できるメソッドシグネチャにはいくつかのルールがあります。その辺りのことはスライドにも書いてあるので一緒に参照していただけると良いかなと思います。

メモリの扱いに関する別の要素としては、RubyGCで不必要だと判断されたらRust側のデータも開放される様にライブラリが面倒を見てくれる様になってますが、Rustの世界での構造体同士の関係をRubyGCでに追いかけることはできないため、ちゃんと追いかけられる様にRubyオブジェクトとしての構成をコントロールするか(インスタンス変数に参照を割り当てておくとか)、RcやArc型などを使ってRust側でメモリが開放されない様な工夫をしないと、予期せぬ箇所でSEGVに繋がることがある、というハマりポイントがあります。

これは本質的にはRustに限った話ではなく、CでRubyGemを書く時もRubyGCが動く時に、実際にはどこを開放して良い領域なのかは考えておく必要があります。ただ、Cだとマニュアルで開放しなければいけないところを、Rustで書くとライブラリと所有権システムがかなり面倒を見てくれるので、楽な反面意識から抜けてしまうという印象がありました。 Rust自体はダングリングポインタなどが発生しにくい様に設計された言語ですが、RubyGCと組み合わさると普通にそういった事象が発生してしまうので、Rustを使っているからといって、それだけで安全になる訳ではないということですね。

何にせよ、GCはやっぱり難しいなと改めて認識できたのは今回の収穫と言えるかなと思います。

最後に、イベント後の懇親会でがちゃぴん先生に、「結局Rustで書くのってオススメなの?それがよく分からなかった」と言われて、確かにちゃんと書いてなかったなと思ったので、自分なりの結論を書いておこうと思います。

あくまで個人的な感想ですが、ネイティブ拡張を書きたいと思った時に、CとRustの習熟レベルが同じ程度ならRust使った方が多分楽だしオススメです。色々な便利ライブラリもCargoで管理してくれて使い易いし、文字列や配列状のデータの様によく扱う割にCだと面倒なことも上手くカバーしてくれますし、メモリ管理もCより確実に楽で安全です。一方でCなら何度も書いてきたし何とでもなるという人が無理してRust使っても、凄く楽になるかというとそうでもない気はします。自分はCもRustも大したことないので、勉強すること自体は色々あったけどRustの方が安全だし書き易いんじゃないかという感想でした。

まあそもそもの話ですが、CやRustでRubyGemを書くということ自体、そんなに頻繁に発生することではありません。今回題材にした様なCで提供されるライブラリのバインディングを書いたり、とにかくパフォーマンスを稼ぎたい時に必要に迫られたりといったケースでないなら、余り必要のない技術です。そういった意味では今回会場で発表を聞いてくれた皆さんの多くにとって、すぐには「役に立たない」話と言えますね。自分にとってさえ、明日仕事で使える様な話かというと全然そんな話ではありません。

しかし、いつ自分の様にやたら大量のデータをRubyで扱う様な仕事に就くか分からんもんです。自分も昔は全然想像してなかったことですが、そういう仕事をしてると、ここで何とかパフォーマンスを稼ぎたい、という状況が発生することがあります。そういった時に使える選択肢を増やしておくことは、すぐには役に立たなくてもいつか役に立つかもしれないなと思っています。

何年後かにそういう状況になったり、ふと思い出してRustの勉強をしてみたくなった時に、この記事と発表資料が力になると幸いです。

今回、登壇機会がいただけて、自分の学習にも色々繋げることができました。大阪Ruby会議04を開催してくれた皆様、一緒に参加してくれた皆様に、改めてお礼を申し上げます。

楽しいイベントをありがとうございました。またRubyコミュニティでお会いしましょう!

RubyKaigi 2024に参加できて本当に良かった

RubyKaigi 2024に参加してきました。

今回参加までに紆余曲折あったので、一時は参加を諦めていたんですが、何とか無事参加することが出来ました。

2011年に初参加して以来休まず参加していたので、ついに連続参加が途絶えるのかと思ってましたが、無事連続参加を達成できて嬉しい限りです。

今回はそういう事情もあってか、コミュニティとの繋がりを強く感じることができたRubyKaigiでした。

色々思いが溢れてしまって、技術的に楽しかったこと、自分が嬉しかったこと、参加前の事情とか全部書いてたらえらい分量になってしまいました。気が向いたら目に付いたところだけ読んでくださいw

参加前

そもそも何があったかというと、大体去年の12月ぐらいから咳が止まらなくなり、更に年明けぐらいに高熱が出た上で咳が出続けている状態でした。

余りに咳が酷かったので、喉に傷が付いた後胃酸が逆流したりして声帯の近くに潰瘍みたいなのが出来て、まともに喋ることが出来なくなりました。

一番酷い時は、水飲むだけで苦痛だったし夜は寝れないし微熱が続くしということで、休職しようかと思ってたぐらいです。

大体その状態が2ヶ月半ぐらい続いて、本当に最悪の場合は咽頭癌の類かもしれんしこれはRubyKaigiまでに回復するか分からんぞ、ということで一旦参加を諦める覚悟をして、予定を立てるのを止めました。

その後、病院で検査した結果悪性腫瘍の可能性はほぼ無いということが分かって、そこからちょっとづつ状態が回復しました。多少心理的要因もあったのかもしれません。回復傾向になるまでは、かなり精神的にもキツかったですね。

3月後半ぐらいから概ね喋れる様になったんですが、体調がまだ不安定だったのでそこでもまだ参加は決めてませんでした。

4月前後で、Asakusa.rbの花見があって、久しぶりに人とちゃんと喋ってみて、なんとか乗り越えられたので、この辺りから行けそうだなという気になってきました。

その後、RubyKaigiのタイムテーブル解説回が4月の後半にあって、行くかどうかも決めてないのに参加したんですが、kaneko.yさんとかtagomorisさんがすげー楽しそうに自分のトーク内容をアピールしてくるので、これは行く覚悟を決めるしかないなと思い、その翌日に予定を立て直しました。

後は、悪化しないことを祈って当日へ、という感じです。

沖縄は飛行機の本数も宿も多いので、4月の終わりに予約を取っても割と何とかなったのが幸いでした。

参加中

今回、そういった事情もあってTwitterを見てくれてた人達からは、えらく体調を心配されると共に「来られて良かったですね」と声をかけていただきました。

会う人、会う人に言われたもんだから、同じことばっか喋ってるなwと思ってしまった。

でも、本当に嬉しく思いました。ご心配をおかけしましたが、ちゃんと楽しむことができましたし、コミュニティの皆さんと交流することが出来て良かったです。ありがとうございました!

その他のハイライトについては、続きで書いていこうと思います。

Day0

会場近くのVAMOSで、MNTSQさんにビールを奢ってもらいつつ、kwappaさん、ryopekoさん、森田さん、makimotoさんといった面々で飲んでました。その後、福岡のjimrockさんと合流して、jimrockさんの昔の知り合いがやってたというお店にタクシーで行き、とても地元民向けっぽいお店で二次会という感じ。

この後も大体毎日イベントの合間にビール飲んで話したりする場としてお世話になった。

煮付け(アカマチだっけ?)がすごい旨かった。後グルクンの雲丹焼きが酒を吸い取る力を持っていた。

kwappaさんと自分の仕事上の立ち位置とか、自分の中途半端さみたいなものについて話が出来たのが良かった。

ryopekoさんと同じホテルだったので、最後にコンビニで缶ビール買って延長戦したのも楽しかった!

Day1

1日目が自分にとって一番セッションが濃かった気がしている。

キーノート

とにかく、ぺんさんのキーノートが圧巻で芸術的でめちゃくちゃカッコ良かった。1000人入るホールに埋まっている聴衆が全員「ん?え?何?」ってなってる空気感を感じることって人生で早々無いだろうなと思いましたね。

使える技術の一つ一つは理解できなくはないが、それをやろうとする発想力・想像力の幅の広さが全然違うんだなということを実感した。自分もそれなりにはRubyで悪さをするタイプですが、次元が違う。TRICK 2025の開催も決まったし、来年が楽しみです。人生で一回ぐらいは応募してみたいがはてさて……。

kanekoさんのセッション

また、自分が参加を決めたkanekoさん、tagomorisさんの両名の発表が1日目にあったので、それもバッチリ聞けたのが良かった。

kanekoさんの発表は技術的にも面白かったんですが、特に凄いなと思ったのがプロジェクトのゴールというか行きたい未来をちゃんと見据えていて、そのために必要なブロックをちゃんと切り出して分かり易い問題に落としていることだと感じました。今回の発表は明確に到達可能な未来が見えていて、それに向かうチームが育ったことをアピールするという目的もあったのかなと勝手に思っています。

ちなみに、自分はRubyのParserのスタンスとしては、処理系の基盤はParser Generatorで生成するべきだと思っていて、kanekoさんの説明に納得している立場です。Rubyは結構APIファーストなデザインで作られている言語だと思っていて、これはRubyを使う人に負荷をかけずに処理系の内側を柔軟に変えられる様にする意図があるのだと思ってますが、一方でAPIファーストで何かを追加しようとした時に文法が収集付かなくなる可能性もありそうだと感じていて、安全にメンテ可能な確かな基盤として、明確な文法基盤がある方が今後のメンテにとって有効なんじゃないかと考えています。

一方、MatzがASTのAPIとしてはPrismを採用するつもりと言っていて、それについても自分は賛成です。Prismはエコシステムの対応状況をかなり意識して作られているというか、現実的な今のユースケースを既にある程度解決している所に優位性があります。RubyPrize受賞の時のKevinさんのプレゼンを聞きましたが、既にParser関連の野良gemの大半を置き換えることに成功しています。Rubyのエコシステムの中で便利に使える共通のASTインターフェースが、処理系の中でメンテされるのであれば、それだけで十分役割を果たしたんじゃないかなあと思うところです。

という訳で、今の自分にとっての理想的な共生関係は、Lramaが処理系のパーサーを担ってそこからCに限らず色々な実装のパーサーを生成可能になっていて、Rubyレベル(もしくはC拡張レベルも?)でパース後のASTを扱うインターフェースはPrismが生み出したAPIでコントロールできる、という状態が良いなと思っています。まあ、一Rubyユーザーによる勝手な妄想ですがw

yujiyokooさんのセッション

初めてRubyKaigiで見た時からファンで、毎回実機のゲーム機と自作のプレゼンツールで発表自体がデモになっているというクールな発表です。 今回はWiiのモーションセンサーをドリキャスに移植して釣りコンのモーションでスライドが操作できる様になってた所が最高に変態だと思ったポイントです。 情熱とロマンを感じますね。

後になって会場でyujiyokooさんとお話しする機会があり、同じ靴(ジャングルモック)の愛用者であることが分かり嬉しかった。相変わらずジャングルモック仲間が多いRubyKaigi。

tagomorisさんのセッション

話は変わって、tagomorisさんのNamespaceですが、これは直近のRubyの中でも一番自分に刺さるキラーコンテンツでRefinements以来のワクワク感があります。

今の理解では処理系内部のクラス・定数構造の基本形をroot namespaceでfixし、main namespace、sub namespaceと段階的にcopy on writeでクラス構造を複製しつつ変更を反映させるという構成だと理解してます。

今回は実装の詳細まで降りる時間が無かった様なので、想像でしかないんですがRubyの定数参照は非常にややこしいのでその辺りの解決と、クラス構造を複製した時に思わぬところから参照が残っていて変更が伝播しなかったり、GCで消されて死ぬというのが難しそうだなと思っています。後、今後の展開で気になったのはObject Shapeとの関係かな。必要なものがコピーされてれば、後は内側で勝手に解決されそうな気はするけど、本当か?という気もする。

Namespaceは、Railsで真面目にモジュラモノリスの区分けを行う上で非常に役に立ちそうです。RDBを共通化しつつコンテキストごとにモデルを区切っても明確に範囲が限定できるはず。また、fluentdの様なプラグイン構造を持ったアプリケーションを作る上では依存ライブラリの衝突は現実的に発生しうる問題で、特にサイレントに微妙に壊れた時が一番困るが、それを解決できそうです。他にはRailsを部分的にアップグレードして影響範囲を確認したり、bindingを引っ張り出して遊んだりしてみたいですね。UnboundMethodを引っ張り出すのは流石にメソッドエントリが一致しないので使えなさそう。

更には、ドラスティックなDSLを実現するのにも使えそうです。一定の処理領域内なら組込みクラスを弄っても変更内容を閉じ込められる。この辺りRefinementsに近いところもある。Namespaceだと変更内容はNamespace全般に伝播するので、Refinementsより便利なこともある一方で、Refinementsはスコープがより限定的なのが逆に良い点になるケースもあります。状況に応じて使い分けていきたい。(Refinementsはモジュールの探索チェーンの動きがちょっと特殊なので、Namespaceの実装時のエッジケースになりそうな気もするがw)

一方で、セッション中でも語られていた様に、gemのエコシステムにおいて古いバージョンが維持される逆向きのインセンティブが発生する懸念は理解できるし、それに関連してbundlerとの統合についても懸念する意見をTwitterで見ました。コミュニティにとってより良い機能にするためには、考えることが色々ありそうで、この辺りの意見交換では自分も貢献できそうに思いました。

参考: サプライチェーンアタックの対策について以前のRubyKaigiで登壇されていたMaciejさんのツイートからのスレッド

こんな感じで実用的かつRubyの柔軟さの幅を広げることもできそうということで自分の中では一石二鳥の機能だと思っていたんですが、Day3のMatzのキーノートで4.0の目玉になる可能性が高まっており、注目度も一気に爆上がりで思ってたよりもっと凄いことになりましたね。

という訳で、今回参加の決め手になった2人のセッションはちゃんと聞けたし、考えることも色々できて本当に良かった。アピールしてただけのことはある。

懇親会

Official Partyのチケットはギリギリで間に合ったので、参加できました。インドア派の自分でもあの会場は最高だったなー。沖縄の海辺でパーティってのは絵になる。

海風で髪がえらいことになってますが、こういう髪質なんですよww

Official Partyは色々な人に挨拶して回って、2次回はアルパカさんとNamespace期待度高いっすねーという話で盛り上がっていたと思う。この辺りから酔っ払ってて細かいことを覚えていないw

Day2

キーノート

Day2のキーノートについては、今回余りRubyKaigiらしくはなかったというか、良いトークだったしKaigi on Railsだったら諸手を上げて喜んでたトークなんですが自分がRubyKaigiのキーノートとして期待する内容とはちょっと違ってた感じですね。個人的には、もっとFiberやFiber SchedulerとFalconの深いところが聞きたかった。まあ、余りハードなトークばかりを求めるのも違うとは理解していて、単に自分の好みの話ですが……。

ydahさんのセッション

Day2で特に印象に残ったのは、ydahさんのLramaの記法拡張に関するトークです。自分は去年のRubyKaigiやKaigi on Railsの参加のKaigiEffectとしてjoker1007/tree-sitter-rbsというものを実装していました。要はシンタックスハイライトに使えるRBSのParserなんですが、これはtree-sitterというParser Generatorを使って実装していて、tree-sitterはJavaScriptDSLBNFに近いコードを記述することでParserを生成します。JavaScriptDSLであることの利点は、JSの関数の組み合わせなので関数で再帰化できるし、パラメーター化もできるし、パターン化もできるということです。

これを自分で触って実感していた後に、Lramaのparameterized ruleの話を聞いて、comma区切りのlistパターンをまとめる例などを見ると、あーこれやったことあるやつだとダイレクトに理解することができました。いやー予習してて良かった。genericの様なタグ付けをしていたら型推論が欲しくなるのも理解できるし、最終的な言語非依存のParser出力のためにはCと結合したアクション記述だと不都合も多い訳で、描いている未来にも納得感はありました。

という訳で、自分の理解度が多少高かったのもあり、とても面白く聞けました。実は自分がそう感じたのには、もうちょっと理由があったのですが、それはDay3で分かります。

午後セッション

後、午後の前半ブロックは疲れていてホテルで仮眠を取ったら思ったより寝てしまい、すっ飛ばしてしまって聞けなかった……。特にhasumikinさんのPico RubyからLramaを利用する話が気になってたんですよね。というのも上記の様にtree-sitterを触ってたこともあり、Lramaから吐いたCのパーサーをtree-sitterのカスタムパーサーとして利用できれば、エディタ界隈はシンタックスハイライトのためのパーサーをメンテしなくて良くなるのでは?とか考えたことがあったからです。それで利用する側としてのLramaの使い方に興味があったんですが、残念ながら動画待ちという感じになってしまった。

LT

今回実はCFP出してたけど落ちました。まあ想像出来る理由はいくつかあるぐらい穴があったのでそれは仕方がない。

ハイライトとしてはkanekoさんの「bisonは一つしかスタックを提供していない」と言う時の楽しそうな顔ですねw 本当に最高だった。流石に高速過ぎて脳が付いていけてたか怪しいところはあったけどw

DrinkupとRubyKaraoke

今回はちゅらデータさんのDrinkupに参加させてもらいました。沖縄民謡を聞きつつ飲んでRuby Karaokeに向けて音楽モードに気持ちを切り替えるのに相性が良かった。豚の丸焼きという凄いものが登場したんですが、流石に脂も凄かったので余り多くは食べられなかった。オードブルを作ってくれたのが、どうやらエンジニア兼イタリアンのシェフというマルチタレントな方らしく、ローストビーフローストポークなど色々と美味しかった。後やっぱ地元で食べるゴーヤチャンプルは美味いですね。

そこからRubyKaraokeに合流。なんか結果的に序盤しばらく居た部屋は、ある意味で凄く「ちゃんとカラオケ」をやってる部屋だったw 盛り上がり過ぎずそれぞれ好きな歌をちゃんと歌う、という感じで割と居心地の良い部屋になったのが良かった。 上海から来た方と米津玄師を一緒に歌ったり、相生ゆらさんに歌を褒められたのが嬉しかったですね。

その後、久しぶりにお会いしたrisacanさん、nappan23さん、mishmashtanさんと合流して、ヒプマイの一人12役を披露したり、一人オオサカディビジョンやったりしてました。喜んでもらえて良かった。盛り上がってくれるととても嬉しい。

Yuryuさんと「あいつこそがテニスの王子さま」を二人でセリフちゃんと言いながら歌えたのも楽しかった。これは10年ぶりぐらいに歌った気がするw

なんかカラオケキングとか言われてたし、どうも噂に尾鰭が付いてる様な気がしなくもないww

RubyKaraokeの時は歌うのに集中してて全然写真取ってなかったので、写真が全然無かった。もし私の写真があったらTwitterなんかで送ってくれると嬉しいです!

その後、流石に病み上がりで無理し過ぎはまずいだろうとちょっと早めに抜けて、帰っている道すがらに、何故か私の様子を見に来た野生のやんちゃさんが飛び出してきた。たまたま会うだけならまだしも、自分を狙って移動している最中にバッタリ会うとは、これが「スタンド使い同士は引かれ合う」というやつかと実感し、そのまま閉店間際のステーキヒカルに直行。二人で締めのステーキを食べることに成功しました。

この辺りで、もうRubyKaigiに来た元は完全に取れたなと確信している。

Day3

RubyKaraokeから戻った後、入眠障害が発動して睡眠に失敗した結果、午前のセッション時間に活動できないという失態。and the Worldがーーー!!とりあえず、黒曜さんのツイートを追っかけることで当日の話題に付いていくことは何とかできた。後ですれ違った時にお礼も言えて良かった。

実はSTORESさんのRuby Quizの3日目のネタに自分の名前が出てました。当日はネタバレになっちゃうので言及しませんでしたが、この記事が元ネタですね。ちょっと嬉しかったw

アーロンさんのセッション と sekiさんのセッション

Day3はアーロンさんの赤黒木の話が面白かった。昔Haskellを練習している時に赤黒木を書いたことがあったんでパターンマッチと非常に相性が良いということが分かっていたので、実装の流れはイメージしやすかった。そしてObject Shapeの性質である、基本的にキャッシュを残し続けて削除は無い、リードが多いので検索効率が大事、メモリ効率を要求するといった特性が、赤黒木のデータ構造自体が持つ性質と非常に相性が良いことが良く分かる良い発表だった。アルゴリズムを学ぶことのパワーが伝わってくる話だった。適切な知識があれば、より安全に効率よく問題を解決できることというのはしばしばあって、そのために使える材料がコンピューターサイエンスの基礎知識に転がっている。自分もまだまだなところは一杯あるが、改めて大事だなと思えた発表だった。

sekiさんのERBの話ですが、聞いてるとやっぱsekiさんのプロダクトはオーパーツなんだよなーと思えて仕方ないですね。captureの解決策とかクール過ぎるし、今から見ても有用性に対する目の付け所が良過ぎる。binding#local_variable_getの実用的な活用法が見れます。

Matzキーノート

MatzのキーノートはやっぱNamespaceへの思いを話してくれたのが熱かったですね。その話題もあってかRuby4が見えてきたかなという印象でした。そして自虐ネタは封印する様にしたらしいw まあガッカリされると言われている理由は分からんではないし、一方でそんなこと言う輩が居るがちゃんと現実を見てから言ってる?みたいなこと言いたくなるMatzの気持ちも分からんではないw

tagomorisさんの仕事の注目度が爆上がりしたのは良いことだけど、これはプレッシャーも凄そうだなーと思う。微力ながら応援しております。(ちょうどこの後の懇親会でJustinと話してた時にそういう話になった)

AfterParty

色々話せて良かったんですが特にハイライトとして覚えているのは、ydahさんと遭遇して話をした時に、ydahさんとkanekoさんの発表の想定リスナーの設定が私(joker1007)だったらしいと聞かせてもらったことです。Kaigi Effectで触発されてRBSのパーサーを書く様な人を狙って話を作れば、面白い話になりそうだし今後の仲間を探すのにも有用だと思ってもらえた様です。実際AfterParty会場でそういったtree-sitterの動きについて理解したことをydahさんに伝えることも出来たし、道理で聞いてて面白かった訳だと思いました。ちゃんと聞きに行けて本当に良かった。

追記: 改めて聞いたらydahさんが金子さんにレビューしてもらってた時にそういう話になったということでした。なので金子さんの方の真の想定リスナーは不明ですw でも近いところにあったのかもしれないなーと勝手に思っている。

後、上の階にあったカラオケ付きのバーカウンターで万葉のkukoさんと念願のカラオケが出来ました。(喉壊してたのにまた歌ってんのかって感じだけどw) 魂のルフランを一緒に歌えて楽しかったー。kukoさんめちゃくちゃ歌上手いし、お酒入ってない時に改めて行きたいなあ。

その後の二次会は、結構人数が居たんですが自分の居たテーブルがやばくて、kateiさん、なるせさん、がちゃぴん先生、ogijunさん、どみにおんさん、という感じでした。LLVM&RubyのコミッターとLinuxカーネル&Rubyのコミッターがそれぞれ自分の仕事はそんなに難しくないですよーってお互いを褒め合っていて、周りが「簡単な訳あるかー!」となってたのが本当にRubyKaigiだなーという感じで楽しかった。

その後、何故か自分がなるせさんにお前はやる気出してコミッタになるんだ!とすげー激詰めされて必死に回避してた気がします。いや、やる気が無いとか無理とかまでは思ってないのですが、心の準備とか覚悟というものがですね……。

まあ、何にせよとてもRubyKaigiらしい懇親会に参加できて最高としか言えない夜でした。

帰りがけの羽田空港で出会った最後のスタンド使いkakutaniさんとのツーショットで締め。出来過ぎだったな。

まとめ

今回、参加自体が自分の中で危うかった経緯もあって色々な人に心配をおかけしてしまいましたが、何とか元気そうな姿が見せられて本当に良かったと思っています。本当に会う人会う人に心配されてたので申し訳なかったw

(実は今もちゃんと回復したとは言えない状態で、喉の違和感や続きそうな痛みには結構注意を払っている状態です。覚悟の上でしたがRubyKaigiでかなりの負荷がかかったことは間違いないので、また状態が悪化しないかしばらく気を張ることになるでしょう。)

また、最近仕事で余りRubyを使わない領域で仕事をしているものでRuby自体とは若干関わりが薄くなっていたのですが、パRailsに関してフィヨルドの生徒の方から今年も沢山の感謝の声を聞かせてもらいましたし、ここ最近での数少ないアウトプットだったtree-sitter-rbsの活動がパーサーギャング団の想定リスナーの材料になったり、これのおかげでVSCodeからneovimに戻れたので本当に嬉しいと万葉のosatohhさんから感謝の言葉を頂いたりしました。

自分としてはちょっとしたことしか出来てないと思ってましたが、ちゃんと誰かの役に立ってたことや伝わっていたことが実感できてとても嬉しかったし、こちらの方こそ感謝しかありません。本当にありがとうございました!

コミュニティの皆さんとの繋がりを改めて実感し、RubyKaigiというのは本当に良いイベントで、自分にとっても大事なものだったなということがよく分かりました。

本当に参加できて良かった。楽しめて良かった。多くのRubyistに会えて良かった。今年もありがとうRubyKaigi!来年も無事参加できます様に!

偏りに満ちたWayland時代のLinux Desktopおすすめアプリ 2024

最近、wayland移行も大分安定して、デスクトップで利用するアプリも多少変化したので、最近利用しているものをまとめておこうと思う。 基本的にGNONEやKDE Plasmaみたいな重厚なデスクトップ環境は使わないタイプなので、そういうラインナップになっている。

開発で必須、みたいなやつはこの記事には余り入れてない。

ウインドウマネージャー

Hyprland

waylandで動作するタイル型ウインドウマネージャー。ヌルヌル動くのが気持ち良いだけでなく、機能的にもよく出来ている。開発が活発なのも良い。 waylandで画面共有のために利用されるxdg-desktop-portalのために独自実装を持っていてswayでは出来なかったウインドウ単位の画面共有が出来る。

という訳でwaylandのタイル型ウインドウマネージャーといえばswayがメジャーだが、最近はHyprlandを利用している。

IME

fcitx5

元々ibusユーザーだったのだが、waylandの入力プロトコルへの対応状況ではfcitxの方が良さそうに感じる。

ブラウザ

Firefox

Chrome及びChromiumはwayland対応が非常に微妙で、XWayland経由で起動しないと日本語を打つのにめちゃくちゃ苦労する。 英語しか打たないなら気にしなくていいのだが。 なので、Wayland環境ならFirefoxを使う方がいい。ベンダーニュートラルなのを応援するのも大事。

ステータスバー

waybar

wayland対応のステータスバー作成アプリ。設定が簡単なのが良い。やたら凝ったことをしないならこれで十分。

デスクトップ通知

dunst

wayland環境でもちゃんと思った通りに動作するし、デスクトップ環境に依存しないのが良い。

ターミナル

alacrittyも悪くないのだが、footの方はwayland専用で若干alacrittyより高速らしい。 sixel対応しているのが良い。

chafa

画像をiterm形式かkitty形式かsixel形式かsymbols(ターミナルの色指定で無理矢理出すやつ)の形式に変換して表示してくれる。 とりあえずターミナルで画像が見たい時は使える。sixel対応のfootだと便利。 とりあえずターミナルで画像を簡単に見たい時に使える。

delta

diffをside-by-sideで出せる様になるやつ

zoxide

autojumpとかzのモダンなrust実装。fzfが入ってれば履歴からも簡単に探せてディレクトリジャンプ出来る。

starship

入れるだけでshellのプロンプトを良い感じにしてくれるプロンプト生成ツール。 相当こだわりが無いならこれで良いと思う。

sheldon

汎用的なshell plugin管理ツール。zshrcの整理に使っている。。

pueue

CLIの実行をキューイングして、任意の並列数で順番に実行してくれる。たまに便利。

シェルのチートシート管理ツール。AWSのコマンドの連鎖とかを記述できるので、aws cliでlist upしてfuzzy finder噛まして検索したarnに対して操作コマンドを実行みたいなスニペットを簡単に書ける。

スクリーンショット

grim + slurp + swappy

slurpで指定したデスクトップ領域をgrimで画像にしてswappyで加工・書き込み。 コマンドにすると以下の様になる。

grim -g "$(slurp)" - | swappy -f -

色々必要でややこしいので、flameshotのwayland対応を使うのも良いかも。

ターミナルファイラー

yazi or ranger

基本的にはrangerで良いと思っているが最近余りリリースが無いので、新しいもの好きならyaziが良いんじゃないかと思っている。 rustで実装されていてやたら早いのが売りだが、MIME特定にfileコマンドを利用していて、NASにアクセスするとそこが遅い。 拡張子だけでMIMEを決めるプラグインがあるのでそっちを使う手もある。

GUIファイラー

thunar

ほとんど使わないのだが、ブラウザなどにD&Dしたい時にターミナルファイラーだけだと困るので用意してある。他のファイラーだとwaylandで動かなかったり、GNOMEとかKDEの必要無い依存がガッツリ入ったりするので、バランス取れてるのがthunarだった。

動画プレイヤー

mpv

これは昔から変わらない。これ一択だと思う。

音楽プレイヤー、オーディオ関連

mpd + cantata

mpdならDSDのハードウェアデコード再生が出来るし、システム移行しても潰しが効きそうってことで採用している。まあ、そんなDSDハイレゾ音源ほとんど持ってないんだけど……。 再生のフロントエンドはcantataを利用している。開発がほぼ止まっているのだが、これより便利なフロントエンドが無い。ここは不安な点の一つ。

qpwgraph

pipewireの接続状態を可視化してくれるツール。GUIで音声出力先を一時的に切り替えたり、pipewireのプラグインとして実行しているノイキャンに繋いだりバイパスしたりできる。

音源タグ編集

kid3-qt

特筆すべき点は無いけど必要十分という感じ。

画像ビューワー

vimiv-qt or nsxiv or mcomix

waylandネイティブで起動できて軽さとシンプルさで選ぶとvimivだった。 wayland対応にこだわらないならnsxivでも良い。 zipにまとめた画像ファイルはmcomixで見ている。

壁紙設定

swww + waypaper

wayland環境での壁紙設定にはそれ用のアプリが要る。 hyprpaperとかswaybgの方がシンプルな気はするけど、壁紙切り替え時にエフェクトが入るという地味かつ無駄なオシャレ感でswwwを利用している。 壁紙の選択はwaypaperがswwwバックエンドに対応していたのでこれを利用している。

mpvpaper

動画ファイルを無限ループする壁紙にしてくれる。live wallpaperで検索するとオシャレな壁紙が手に入る。 タイル型マネージャーを使ってると、正直リソースの無駄なのだが、オシャレ感が欲しい時にw

ランチャー

wofi

シンプルで応用が効くし、そもそもがwayland向けなのでrofiみたいにwayland forkを利用したりする必要がない。 desktopエントリの起動だけじゃなくてPATHにあるコマンドの起動にも使える。

クリップボード管理

wl-clipboard + cliphist + wofi

wl-clipboardで変更をwatchして変更があったらcliphistに溜める。 キーボードショートカットでcliphistから読み出したものをwofiに渡して表示する。選択したらそれをclipboardに書き出す。という感じ。 ただ、cliphistに溜めたやつを細かく制御できないので、パスワードマネージャーからコピーした値などにセキュリティ上の懸念が多少ある。

Bluetooth

blueman

linuxBluetoothを使うなら、まずbluezが必要でそのCLIだけで十分な場合もあるが、GUIで操作したい時はbluemanを使っている。 GTKに多少依存しているぐらいでシンプルなのでデスクトップ環境を選ばないのが良い。余計なものを余り入れなくていい。

プレゼンツール

pdfpc

PDFをkeynoteのプレゼンモードみたいに表示できるツール。発表者メモも書ける。

VPN

tailscale

これを入れておけば困ることはない。革命的に便利過ぎる。個人用途なら大抵の場合無料で十分。200台ぐらいデバイス持ってる変態はお金出してくださいw

20年Rubyを触ってきて初めて踏んだattr_readerのケツカンマ問題、あるいはdefの返り値がシンボルであることの問題

今日Rubyを書いていて、なんじゃこれと思った動作があった。

試しに以下のコードを実行してみて欲しい。

class Foo
  attr_reader :hoge, :fuga,

  def initialize(a, b)
    p a
    p b
  end
end

Foo.new

実行してみると分かるが、これには例外が出ない。

initializeで定義した必須引数はどうなったのか?

よくよく見るとattr_readerの引数の末尾に,がある。

つまり、このコードは分かりやすく書くと以下の様になる。

class Foo
  attr_reader(:hoge, :fuga, def initialize(a, b)
      p a
      p b
  end)
end

Foo.new

このコードを更に分かり易く書くとこうなる。

class Foo
  def initialize(a, b)
    p a
    p b
  end

  attr_reader :hoge, :fuga, :initialize
end


Foo.new

initializeが定義され次の瞬間にattr_readerで上書きされてdef initialize; @initialize; end相当の状態になる。

そりゃそうだという感じだし、何もおかしなことはない。ないのだが!これは結構怖い。

という訳で、attr_なんちゃらの引数のケツカンマには気を付けよう。 (というかシンボルを受け取ってメソッドを定義する類のメタプロコード全般)

自分で書く分には意外とこうはならないのだが、copilotみたいな奴の補完でざっくりと書いてしまうと、ケツカンマ以外は問題無い上にシンタックスエラーも出ないので、「はあ?!」という動きになってしまう。というかなった。

バルダーズゲート3が素晴らしかったのでオススメポイントを書く

年末からプレイしていたバルダーズゲート3をようやくクリアした。大体130〜140時間ぐらいかかったと思う。最近のゲーム体験としては、トップクラスに入る神ゲーだった。ここ数年だとOuter Wildsに匹敵するレベル。ゲーム史に残るRPGと言っても過言ではない。

ただこの作品は、とにかく序盤がハードなのと自由度が異常なので、慣れない人にはかなり取っ付き辛いらしい。それを差し置いてもオススメしたいので、どういうところがオススメなのかと何がハードなのかをちょっと書いてみようと思う。

前提として、バルダーズゲートシリーズはTRPGのダンジョンズ&ドラゴンズの世界観に基いたゲームで、ちなみに自分は元々D&Dは存在ぐらいは知っていたしTRPGについてはある程度の知識と経験がある程度である。

結論から書いておくと「自由度」と「分かりやすさ・親切さ」のトレードオフを受け入れられるかどうかが、このゲームの肝だと思う。

TRPG感の再現

流石にコンピューターゲームなので限界はあるのだが、会話や行動の選択に対してとにかく自由度が異常に高いし、その場の会話だけでなくちゃんと続きの展開や有利不利に影響する。そして場合に依っては取得している技能によってダイス判定が入る。得意なことはキャラごとに違うので、仲間と旅をすることにちゃんと意味が感じられる。会話の流れ次第では良い人間にもなれるし、めちゃくちゃ嫌な奴にもなれる。ちゃんと一貫したキャラ設定を想像してプレイしていると没入感が高まってとても良い体験が得られる。この辺りのロールプレイ感はTRPGの良さを上手く活かせてるなあと感じる。

悪事的なことだと、めちゃくちゃ高値を吹っかけてくるキャラの話を一回断わった後で部屋に忍び込んだりスリで入手したアイテムで突破したり、商人にワイロを渡して評価を上げた後にスリで金を取り戻すとか悪辣なこともできる。こういう自由度に対して頭を捻るのが楽しい。ペテンにかけて信用させてから後ろから刺すとか。

一方で、分かり易い解決策は必ずしも楽ではないし、報酬が良いとも限らないので、効率プレイ大好きみたいな人には余り楽しくないかもしれない。

探索とクエスト展開のバリエーション

バルダーズゲート3は昨今のオープンワールドRPGの様な探索とサブクエスト受注みたいな流れはあるのだが、その手のゲームの様にマップ上に依頼者のマーカーが出てたりとかは一切無い。町の人と喋ってたらいきなり問題に巻き込まれたり、頼みごとをされたりするし、その辺に落ちてるメモからクエストが開始することが多々ある。(目的地が明確な時はマーカーが出る)

なので、クエストラインの途中から合流したり、いきなり解決に近いところから始まったりするし、あるクエストが別クエストの目的と最終的に重なるケースもある。なので開始時期がズレるとイベントそのものが消失したり、報酬を貰い損ねたりする。

これも自由度とある意味で不親切な所のトレードオフで、自分としては世界を探索してそこに関わっている感じが強く感じられるので好きだったが、この点に関してはクエスト発生ポイントに関しては攻略情報を確認して、後は流れに任せるみたいなやり方がバランスよく楽しめるのかなーと思う。

めちゃくちゃ長い期間継続するクエストとか、探し物をするクエストは実際何も攻略情報が無いと、いつまでかかっても終わらないみたいな状態になるので、諦めてある程度割り切った方がいい。

とにかく選択を迫られたり取り返しがつかないことが多いので、いやそういうつもりじゃなかったとか、ここでそのダイス運は勘弁してくれ、というのを避けるために手癖でクイックセーブする様にしておくことをオススメする。盗みに入る前にセーブしとくとかちょっとズルいけど自分としては許容範囲。オートセーブポイントはかなり少ないので、セーブを忘れているとやり直したくなっても結構前に戻されたりする。まあ相当後になってからじゃないと選択の結果がどうなるか分からないケースもあるので、そういう時は諦めよう。

(マックス難易度だとシミュレーションゲームの鉄人モードみたいなのがあって、セーブ&ロードが禁止されるという非常にシビアなモードもある)

戦闘のシビアさと戦略性

戦闘は昔のゲームだとタクティクスオウガとかFFタクティクスに近い。ターン制で位置を移動して近接、遠距離、魔法などを選択して戦う。高所の概念や明るさや地形効果などもあるし、その辺に落ちてるオブジェクトも大抵攻撃可能だし、即席攻撃というシステムで拾って投げたりできる。敵の死体をぶん投げることもできるし、高所から突き落とすこともできる。

普通に殴って魔法撃ってもちゃんと楽しいが、濡らして電撃かけるとダメージ2倍になったり、火薬に火炎ぶつけて爆発させたり、飲んだくれてるコボルトの零したアルコールに火を付けるとか、そういう楽しみ方が出来るので、戦略性もかなり高い戦闘が楽しめる。

敵の近くで隠れて、沈黙フィールド貼ってから暗殺したり、バフかけてから奇襲して有利に進めたりできる。逆に敵が待ち伏せしていて知覚判定に成功しないと、見えないとかもある。

一方で、そういうことに関するガイドは余り無いので、何が出来るかは多少攻略情報を参照した方がいいかもしれない。

この作品では、特に序盤の戦闘がきついのが挫折を生みがちなポイントである。ラノベゴブリンスレイヤーではないが序盤のゴブリン集落に何も考えずに突っ込むと、難易度ノーマルでも余裕で全滅するし1戦1戦で全力出さないと普通に死ぬぐらいの戦闘になる。特に数の暴力に負けるので、序盤から上手く状況をコントロールする必要に迫られるのだが、序盤なので何が出来るのか自分が分かってないという辛い状態が続く。

多分、ここで心折れる人が多少居ると思うのだが、ここを突破すると割と楽になるので、諦めるのは勿体ないなと思う。

これに関しては、諦めて難易度を一時的にイージーまで下げるというのもアリだと思う。自分は最初昨今のゲームの難易度だとハードぐらいがちょうどだろと舐めてかかったら、全然無理でノーマルに変更してギリギリみたいな感じだった。まあ、これもやり応えの裏返しみたいなものではある。

中盤移行はやれることが増えて、強力なバフも使える様になるので慣れてくると大分戦闘は楽になる。基本的に守るより攻めた方がいい。先手必勝でとにかく数を減らすか強敵をぶちのめすのが定石だと思う。

後、何回かやらかしてリトライした点として、盗みがバレて戦闘になったり、範囲魔法でNPC巻き添えにするとそのまま死んで取り返しが付かなくなったりするので、その辺りはちゃんと考えて戦う必要がある。

序盤に覚えておいた方がいいTips

戦闘に関わらず、このゲームはRTボタンのメニューからいつでもターン制モードに入れる。隠密行動で仲間を順番に移動させたり罠を解除するまで仲間を待たせたり、敵の視線を回避する時には必須なのだが、自分は最初これを知らなかったので、無駄に罠を踏んだりした。

また、戦闘中に仲間のターンが連続している場合、行動順は自由で行動完了前に切り替えたりできるので、先に移動して位置をズラしてから範囲攻撃とかも出来る。これも最初気付かなくても行動を無駄にしたりしていた。

クラス選択とレベル

キャラメイク時にクラスを選択するのだが、クラスの選択によって基本能力値が決まるシステムになっている。クラスの幅は結構広いのだが、このゲームは魔法がとにかく分かりにくい。使う魔法を習得魔法の中から「準備」して利用可能状態にしておかないと使えないクラスと、常に習得魔法全てが使えるがそもそも習得できる魔法数が少ないクラスとがあり、また魔法使用回数を回復する方法もかなり限られている。なので、使い所はある程度考える必要があるし、クラスによっては習得魔法を厳選しないと非常に辛いことになる。

この辺のシステムは、TRPGのD&Dの方のシステムをある程度踏襲した作りになってるのかなーというイメージ。魔法スロットや事前準備の概念はその辺から来てると思う。この辺りもゴブリンスレイヤーの術士のイメージが近い。(もちろんD&Dの方が元祖だが)

また、このゲームは最大レベルが12なので、1レベル上がるだけでめちゃくちゃ強さが変化する。特にLV5〜6ぐらいになるとかなりスキルが揃うので、大分便利になる。

クラス選択では基本クラスに加えて、レベルアップ時に別のクラスを取得してレベルを上げることができる。12LVの中でやりくりすることになるので、最終的にファイターLV9とローグLV3みたいなことになる。シナジーの大きい組み合わせもあるので、特化したスキルが覚えられなくなるが上手く組み合わせるとめちゃくちゃ強くなる。まあ強さを重視すると、ある程度お決まりのパターンはあると思う。投擲バーバリアン+ローグ(シーフ)とかモンク+ローグ(シーフ)とかめちゃくちゃ強かった。

そして、100ゴールドでリスペックするシステムがあってクラス変更とレベルの上げ直しが可能なので、かなり簡単にスキルの組替えが出来るので、中盤ぐらいからガンガン状況に合わせてクラスを変えたり、各クラスで出来ることを調べて自分なりのビルドを考えられる様になる。流石に、色々なクラスで何が出来る様になるのかを自分で全部調べるのはしんどかったので、クラス情報に関しては中盤辺りからめちゃくちゃ攻略情報を参照した。

後、主要な登場キャラのクラスチェンジも可能だが、主要キャラのシナリオとクラスが関連しているので、主要キャラのクラスをゴリゴリ変えるとちょっと世界観に影響が出るかもしれない。自分は基本クラスは変えない様にプレイしていた。

前述した様にクエスト展開の自由度の高さで、戦闘以外の技能や魔法が有効なケースもかなり多いので、戦闘特化以外のキャラ育成や万能タイプのキャラを作るのにもちゃんと意味があるのはとても良い点だったと思う。特に盗みと会話スキルはめちゃくちゃ利用頻度が高いし、動物と会話したり死体から情報を得る魔法もしばしば便利なので誰かが使える状態になっていると有用な情報やアイテムの場所が分かったりする。この世界の動物はかなり賢いので喋ると結構面白い。やたら哲学的な猫とか、100年ぐらい生きてそうな牛とかが居る。

世界観の理解の助けになるもの

正直、D&Dを今からがっつりプレイするのは、そういうコミュニティに居ないとかなりきついので、wikipediaでD&DのForgetton Realmという世界について調べて世界設定やゲームシステムやアラインメント(人格特性みたいなもの、所謂「秩序」か「混沌」か、「善」か「悪」かの概念)について調べておくと取っ付き易くなると思う。他には話に結構絡んでくる九層地獄の設定とか。

後、去年だか一昨年だかに公開されていたD&Dの映画は世界観がほぼ共通なので、プレイ前後で映画を見ると共通の語彙や人名が出てきて面白い。例えば映画に出てきたメインキャラのウィザードの祖先のエルミンスターという大魔導師の名前とかハーパーとかサーイ人がどんな奴らかとかは、ちゃんとゲーム中に出てくるし、ドルイドの能力とかも共通している。ゲームをやってから映画を見ると、かなりD&Dの要素を映画に詰め込んでることが分かって相乗的に楽しめた。

総評

簡単にバルダーズゲート3の良い点と取っ付き辛い点や分かりにくい点について書いてみたが、全体としてこのゲームは本当によくできていて、自分の中ではRPGというジャンルでは現状最高峰に位置するゲームだったと思う。特に序盤の辛さで心折れると非常に勿体ないなと思うので、頑張って乗り越えてプレイして欲しい。

ゲーム的な問題点として大きかったのはセーブ&ロードが遅い点。結構ロードしたくなるケースは多いしリトライしたいことも多いんだけど、ここでちょっと待つのでプレイ時間が伸びる。結構これで時間食ってると思う。スパイダーマン2の異常なファストトラベルの速さとかを体験した後だったので、これに関してはちょっとストレスがあった。

後は大抵が「自由度」とのトレードオフなので、冒頭にも書いたがそこを楽しめるかどうかが一番重要なポイントだと思う。自分としてはゲームというのは自分で操作しているから良いのであって、そこで自分なりの関わり方を選択できるこういう作品はゲームの醍醐味を十全に活かしていてとても良いと思う。

久しぶりに人に語りたくなるゲームだったので、珍しくこういうネタで記事を書いてみたが、気になった人は是非プレイしてみて欲しい。(実はOuter Wildsの時も語りたいことは一杯あったのだが、何書いてもネタバレになりそうで「とにかくやれ」としか言えなかったw)

tree-sitter-rbsのサポートがnvim-treesitterにマージされました

先々週ぐらいからちょっとづつ作業してテストケースを追加したりしていたtree-sitter-rbsの実装が一段落して、普通に使う分には大抵のrbsはパースできるだろう、というところまで出来たかなと思ったので、nvim-treesitterにパーサー追加のプルリクを出しました。

爆速でレビューしてもらえたので、プルリク出してから数時間で無事マージされ、tree-sitter-rbsが簡単に導入可能になりました。 🎉

久しぶりにちゃんとOSSっぽいことやったかなという感じです。

nvim-treesitterはneovim上でtree-sitterで構築されたパーサーを使ってシンタックスハイライトを行う時に必要になるものです。 従来の正規表現ベースのhighlight記法よりも、より文脈に依存したシンタックスハイライトが可能になったり、シンタックスツリーに対するクエリが可能になることを利用したアウトラインプラグインやコードブロック操作のプラグインに対応可能になります。

(既にめちゃくちゃ利用されてると思うんですが、一応neovimの機能としてはまだexperimentalではあるらしい。)

今回、nvim-treesitterの中にtree-sitter-rbsの情報が取り込まれたので、nvim-treesitterのTSInstall rbsコマンドでrbsのパーサーがインストールできる様になり、追加のプラグイン無しでリッチなシンタックスハイライトが使える様になりました。

neovimでrbsを書いてrubyに型を付けたいという方は、是非試してみてください。(まだバグがありそうな気はする)

ちなみにrbsにはannotationという仕様があるんですが、どうやって使うものか全然分かってないので、この記法だけはまだサポートしていません。どうもsteepにあるサンプルを見る限りでは、メソッドの頭に付与して副作用の有無といったメタデータを指定する類のものの様ですが……。