日記ってすごいかもしれん

最近はとある人のとあるブログの記事をずっと読んでいる。記事数がかなり膨大で、11年くらい前からずっと定期的な投稿が出来ているように見える。意識的なアウトプットというやつなのかな?その人はWeb系のエンジニアで、所属を意識できるような記事がたくさんあった。へー、ここにインターンに行ってる人なのか、とか、ここで働いてたんだ、とか。その所属での出来事も書いてくれていて、社員との交流だったり、登壇のことだったり、その支援だったり。今はフリーランスになって、週休3日?4日?でゆっくり暮らしているらしい。バリバリな技術記事と共に存在する個人的な記事たちをざっと追うだけでその人の10年間を追体験しているような気持ちになった。

最近自分がブログに熱を入れているのはこの間記事にしたような言語化についての練習が主だけど、側面としては上に書いた人たちへの憧れみたいなところもあったりする*1。僕もアウトプットの方法としてのブログを大事にしていきたい。今はとても楽しいというだけで記事を書いている、もしこれが上手いこと積もっていくのなら、そのうち大きな証跡みたいになるのだろうか。そうなるといいですね。

上に挙げた人のブログとか、そうでなくとも今からは離れた時代に書かれたような文章をインターネット上で見つけるとかなり嬉しくなるよね。その記事が残っていたというアーカイブ的な素晴らしさ、アップデートが不要なプリミティブな考え方、など。小さな身内での事件を記してくれた記事とかは傍から見る分には純粋な読み物として面白く、かつ学びがあることも多い。また、そういう人が未だに文章を書き続けているとなおさら嬉しくなる。「古から今までインターネットのおもしろを支えていたのはこの人だ」みたいな。生き証人みたいでかっこいい。もちろん「自分が深くに潜って文章を発掘したのだ!見つけた!」というdig的な気持ちもあるけど、まぁどうであれ、文章などのコンテンツは基本残ってくれているだけでとても嬉しい気がする。

界隈の人間がなにか行動をした際に「またそういうことをしているのか、一体何度同じ問題を繰り返すのか」と後ろ指を指されている場面を見たのだけれど、そういうのって過去起きた事象を記して後世に伝えなかったその界隈にも多少の問題があるのではないかと思っている*2。そういった当事者意識を持ちたいよね~、という大きな話ではないが、記事を書くことによる副次的な部分としての、もしかしたら何かの肥やしになるのではないか?という小さな意義をほんのちょっとでも良いから感じていきたい。

*1:さらに別の側面として、僕はみんなの文章が読みたくて、であれば表立ってまず自分が文章を書いていったほうが良いよね、という気持ちでやっている。僕よりも文章を書いている友人と話していた時にそういう感じになった。もしみんなが文章をたくさん書くようになったら嬉しいな~

*2:もちろんこの問題は難しい前提で、そういう面もあるよね~くらいで捉えてほしいです

Cart Racerを2回クリアした

1回目

友人の家でレヴュースタァライトの映画を見させていただいたあと、いろいろあってCart Racerをやることになった。僕は代わりにGetting Over Itを勧めた。

おじさんを操作して買い物の時に使うようなカートに乗り込み、設定されているゴールに向かう、というゲーム。1ステージに3つゴールが指定されていて、4ステージあった。

この操作感がかなり独特でやばかった。だからこそのB級ゲーム*1なのだとは思うが、あまりにも大変だった。

先程書いた「カートに乗る」がまず大変で、WASDとジャンプボタンを駆使してかごを乗っける部分にがんばって乗らないといけない。「乗る」という操作があるわけではなく、「結果として乗ってしまった」というのが正しそう。

基本カートは坂の上に設置されており、おじさんが乗ると同時にストッパーが外れて進み始める。カートが進んでいる間はAとDで左右のハンドリングを効かせられる。が、これもまた難しく、左右にハンドルを切る度にカート自体も左右に揺れ、軸がブレる。これによってカートの滑り具合が増してしまい、とても曲がりづらくなる。さらに大きくブレると普通に横転する。

そんな状況の中、床に設置された矢印(踏むと加速する)を経由してジャンプ台を踏み、空を飛んでゴールに到達しなければならない。勿論空中での操作は効かないので、ジャンプ台に入るまでの行動調整でこれを達成する必要がある。本当に大変だった。

最後のステージの最後のゴールが最も大変だった。そのゴールに挑戦するまでのattempt数より、最後のゴールに費やしたattempt数の方が多かった。このステージは実際に難しいらしく、Cart Racerに慣れたプレイヤー達も声を揃えて「ここだけは運ゲー」と仰っていた。

所要時間は1.1時間。どうやら初プレイにしては短い方らしいのだが、心労としては大変大きな負担となっていた。いや~大変だった。

2回目

これ以後、身内のDiscordサーバーでCart Racerが流行ってしまった。レヴュースタァライトを見せてくれた友人がサーバーのメンバーに配ったらしい。上述した自分のプレイは友人のアカウント上でのものなので、まだ所有していなかった自分のアカウントにも配られた。

これによってサーバーのメンバー(5,6人くらい?)がみんなでCart Racerの初プレイ並走を行う、というイベントが発生した。僕と同じくして1時間ほどでクリアする人もいたが、ひどい人は6時間半ほどぶっ続けでプレイしていたらしい。最後のステージのattempt数は4000台を突破していた。恐ろしい。

繰り返しになるが、上述した自分のプレイは友人のアカウントでのものだった。そのため、自分のアカウントではクリアしたという証明となりうる実績がひとつも解除されていない。これで尻尾に火がついてしまい、再度プレイすることとなった。

ただ、結果としてはかなりあっさり突破することができた。時間としては前回の半分ほどの32分。

最後のステージに至ってはattempt数20台でクリアできた。なんなら最初のステージとふたつめのステージそれぞれで80回くらいやり直しているので、そちらの方がつらかった。気持ちとしては前回のプレイ時よりはまともなカートハンドリングが実現できた気がするが、クリアについては純粋な実力によるものなのか、極端に運が良かったためなのかは判別がつかない。

また、各ステージでのattemptがどれも100回未満だったので、実績「100 attempts」が達成できていなかった。最後にもう一度起動しなおして*2、Rを99回押すことで、晴れて全実績を解除した*3

もう一度やりたいか?と言われるとちょっと厳しい。上達が感じられるタイプの死にゲー(例えばSMWのkaizoハックや、Getting Over It、Pogostuck、Celeste)はさらなる上達を求めて自ら進んでプレイできるが、このゲームをその枠に入れられるかどうかはちょっとだけ微妙なところがあり、それが引っかかってしまう。それでももっとやったらプレイングスキルだけでステージをねじ伏せられるようになるのかなぁ、でもよくやってる人々は軒並み「最後は運ゲーだ」って言うんだよなあ、、、

人にはどのように勧めるべきだろうか、「理不尽に負けない心を獲得できます」とでも言おうかな、理不尽に負けない心を獲得できるのでそうなりたい人はやってみてください。

*1:Bennett Foddyが定義するところのB級ゲーム、具体的にはアセットの寄せ集めだったり、素晴らしいものを公開することよりも作る過程自体が楽しまれたようなもの

*2:Cart Racerは一度クリアしてThe ENDを迎えた後にタイトルに戻ることができるのだが、タイトルのボタンが機能しなくなるバグが存在する。そのため、クリア後にもう一度プレイする場合は一旦再起動を挟む必要がある。

*3:各ステージをクリアした実績とこの実績の他には実績が存在しないようだった

マクドナルドのホットチョコレートを飲んだ

一人暮らしをしてからずっとマクドナルドにお世話になっていて、公式Twitterをよく見ている。これ故、新しいメニューとかは結構すぐ気になってしまって優先的に買ったりしている。

最近ゴディバとのコラボメニューとして、期間限定でホットチョコレートが登場した。

www.mcdonalds.co.jp

くっそうまそ~~~~のみて~~~~~~~~~~~~ 甘い系のコラボメニューでマックカフェ専用でないもの久々に見たな、こんなのは頼むしかない、、、

自分はフルリモートで働いているので、外に出たくないという気持ちが加速してUberEatsで頼むようにしている。が、なんとこのメニューはUberEatsでは頼むことができなかった。マックデリバリーのアプリでも注文することができなかった。どちらも、そもそもメニューの一覧に存在しない。

↑これはTLで聞きかじった情報

↑悲しんでいる…

なぜこのメニューはデリバリーの対象にならないんだ……期間限定のバーガーとかナゲット、デザートは普通に一覧に載るし、運びにくさという点を考えてもホットコーヒーが注文できる時点で問題から外しても良さそうだよな……

そしたら商売的な方法なのだろうか、店舗に出向くしかないのか、ぐぬぬ悔しい……

というわけで1/20の夜にマクドナルドの実店舗に向かった。しかし

なかった…メニューには「ご好評につき終了しました」って書いてある…

これ、そもそもお店で取り扱われていない可能性があるな、だからUberEatsでも一覧になかったのだろうか?とか思っていたけど、翌日の昼になって直接電話をしたら全くそんなことはなかった。おやつ時くらいに遅めの昼飯としてマックに向かった。

こうして無事飲むことができました。おめでとう、よかったね

評価

これ、まあおいしいけど、ちょっと高くない?Sで300円、Mで400円はちょっと…

追記

昼に電話をするのは本当に良くないらしいです。ツイッターで怒られた。申し訳ない。

AGDQ2022を見た

AGDQ2022を見た。

AGDQ2022についてのかるーい説明をする→ 海外で毎年初めに行われているゲームのRTA系イベントで、1/10~1/16のあいだ、ほぼノンストップでゲームのRTAが繰り広げられる。このあいだ書いたRiJの元ネタとなったイベント。

Japanese Restreamという公式ミラーが存在し、こちらでは日本語で解説を行ってくれる。ただ、元配信に音声を乗っける都合上、元配信側の音声はとても小さくなる。コンプレッサーの外部ソースとして日本語の音声が入っている感じ。

解説終わり、ここからは感想

三連休はやることがあって見れなかった。ほとんど1/11から見始めた。

Japanese Restreamで見たり、そうでなかったりの五分五分だった。さっき書いたとおり、元ストリームのほうが音がよく聞こえるため。

アーカイブYouTubeチャンネルにある。日本語解説のアーカイブこちらから

見たかったけど見逃したやつ

  • DKC2
  • Megaman 2
  • kirby 3
  • supermonkeyball
  • Crash Team Racing

パズルボブル4

  • 実家にこのシリーズのゲームが複数本存在するので見てみるか~程度の気持ちで見た
  • うますぎワロタ
    • このゲームは壊れがなく、純粋にうまさを見ることが出来た
      • このゲームは本当に難しい
      • 僕は難しすぎるステージについては自滅して点線のガイドを得ていたが、このレベルだと既に心眼で見えているらしくて点線があってもなくてもあまり関係ないらしい
    • マップ理解やルート構築の点のうまさももちろんあるが、それよりも純粋なパズルボブルのうまさが光っていた
  • WRおめでとうございます
  • お誕生日もおめでとうございます

www.youtube.com

https://www.twitch.tv/videos/1260950431?collection=kS2Kki9uyRa0TQ

スーパーマリオギャラクシー

  • レース形式だった
  • このゲームって60点の動きは誰でも出来るけど100点となると途端に操作が難しくなる印象があるんだよな
  • 特にルイージとかは大きな動きゆえの可動域が広くてspeedrun映えが凄いよね
  • マリオシリーズ特有のベース内移動がスムーズすぎてすごい

www.youtube.com

https://www.twitch.tv/videos/1260970425?collection=kS2Kki9uyRa0TQ

Pumpkin Jack

  • 全然知らんゲーム
  • ゲームがかなり面白そう
  • 壁をダイナミックに抜けていくのは最高
    • これ殆どの壁を抜けられるのかな?
  • Celesteよろしくな感じのダッシュ
    • 斜め下方向、床にプレイヤーを叩きつけて前方向のスピードを得る
  • 上空高くに移動することでイベントをスキップする
  • なんだかわからんが超速で水平に移動して本来向かうことが出来なさそうな対岸まで移動する、など、3Dあくしょんげーむみたいなショートカットがたくさん見られた
    • オブジェクトとの当たり判定とかそういう感じの
  • 走者が「ガッハッハ」と笑うタイプでとても好き

www.youtube.com

https://www.twitch.tv/videos/1260970440?collection=kS2Kki9uyRa0TQ

Hitman: Codename 47

  • 決まった場所に進み、決まった場所を撃つことでターゲットが死ぬゲーム
    • こんな殺し屋が居たら本当に恐ろしい、悪いことをするのはやめてまっとうに生きよう
  • 顔のポリゴンが潰れててワロタ
    • 写しちゃだめだよこれ

www.youtube.com

https://www.twitch.tv/videos/1260970421?collection=kS2Kki9uyRa0TQ

Serious Sam 4

  • 初っ端すげースピードで飛んでってワロタ
  • しかも普通に壁登ってってるし
  • オブジェクトの間を縫っていったり、挟まれたりするときの挙動がRTA向きで良い
  • ゲームが超大味っぽかった
    • RTAだからかな?とも思ったけど、多分元からそうなんだと思う

www.youtube.com

https://www.twitch.tv/videos/1260970432?collection=kS2Kki9uyRa0TQ

みんな大好き塊魂

  • RiJに続いて本当に素晴らしいプレイをありがとうございます
  • harutomoさんかわい〜〜

www.youtube.com

https://www.twitch.tv/videos/1263686926?collection=kS2Kki9uyRa0TQ

ザ・タイピング・オブ・ザ・デッド

  • ゲーセンにある、車型の筐体に乗ってゾンビを銃で撃つやつのタイピング版
  • ゾンビについてる英文をタイピングする
  • チャット欄が全員ゲームやってて最高
    • 僕もやってたけど英文を打つの普通に難しい
  • RTAとしてはタイピングの正確さや、スコア調整のような挙動が結構映えた
  • チャット欄をここまで大きく巻き込めたのはまさしくAGDQ映えというやつなのでは 素晴らしい

www.youtube.com

https://www.twitch.tv/videos/1263686914?collection=kS2Kki9uyRa0TQ

Sonic robo blast 2

  • DOOMと同じエンジンを使用しているオープンソースソニックの3Dのゲーム
  • これファンメイドなの!?
  • これ1998年生まれなの!?
    • 同い年だ、、、
  • 普通にこのゲームやってみたい
    • テンポがかなり良さそうだし、ステージもそれなりに広くてギミックも多くて
  • ダメージブーストとか壁抜けとか激しい
  • ステージがこんなに広いのにおおよそ把握してるのすごいな~

www.youtube.com

https://www.twitch.tv/videos/1263686913?collection=kS2Kki9uyRa0TQ

ShootMania

  • 元々はFPSっぽいが、ゲームの特性を利用してユーザーがつくったコースをいち早くクリアしていくという趣旨のタイムアタックの文化があるらしい
    • スタミナが続く限りは壁キックができる
    • 銃を打つ反動がでかい
      • うまく続けさせれば空中を散歩できるくらいには
  • そんなコースがコミュニティにはたくさんあって、そういったコースから選別したものを数個走る、というものだった
  • 1コースが10分くらい続く中、ずっと難しそうなテクニックを披露し続けていた
    • 進行方向とは逆の方向を一瞬向いて射撃をし、その反動で進行方向へのスピードを得てまたすぐ進行方向を向き直す、とか
    • 僕にはぜったいできん

www.youtube.com

https://www.twitch.tv/videos/1263760626?collection=EDmMkO9FyhY5Vg

TrackMania

  • 結構見たかったやつ
  • レースゲーム
    • 見たことあるとすぐ伝わるけど、レースゲームとしての自由度というか振り幅がとても大きい
    • 様々な種類の床の存在
    • 割と簡単にコースアウトしてしまうがゆえのひどいショートカット
    • など
  • 勿論こちらもユーザーが作ったコースをプレイする文化が発達している
  • 今回はそんなコミュニティのコースのうち選りすぐんだものを連続でプレイしていた
    • 勿論凄い
  • 最後の動画で紹介するコーナーもかなり良かった

www.youtube.com

https://www.twitch.tv/videos/1263760637?collection=EDmMkO9FyhY5Vg

スプラトゥーン2

  • トゥナス
  • 僕はこのゲームが本当に苦手なんですが、そういったゲームでここまで完璧といえそうなキャラコンを見せられるともはや感嘆の声しか出ないな〜ってかんじ
  • なんならステージ自体のスキップとか見てて楽しすぎる
  • 途中で配信が途切れたのかわいそう、仕方がない

www.youtube.com

https://www.twitch.tv/videos/1263760614?collection=EDmMkO9FyhY5Vg

Half-Life 2

  • Steamのゲームだ!ってなった
    • やったことない
  • 最初から最後まで壁を抜け、空を飛び、NPCの頭を叩いてたい印象
  • オブジェクトを持ち、空中で離したそれを踏んでジャンプし、またオブジェクトを持ち、空中で離したそれを踏んでジャンプし、

www.youtube.com

https://www.youtube.com/watch?v=mAgqYvtJYrg

Keep Talking and Nobody Explodes 完全爆弾解除マニュアル

  • modあり
    • 100個のモジュールを
    • マニュアルを見ることなく
    • 一人で解く
  • 凄すぎワロタ
  • 効率化を極めるためなら一人の方が都合が良い、みたいな局所解の極意に見えた
    • RiJのダブルプレイを検証してくれる友人がいなかった、のあれみたい
    • とんでもねえモンスターが生まれてしまったよ

www.youtube.com

https://www.twitch.tv/videos/1263773740?collection=EDmMkO9FyhY5Vg

StepMania/NotITG

  • LITERALLY INSANE
  • 一見の価値あり
    • 本当に
  • やっている譜面が凄すぎる
    • modという種類の譜面がある
      • 譜面のレーン自体に様々な細工を導入できるもの
      • レーンを自由に動かすことが出来るようになる
    • これが本当にすごい
      • 裏に映像のようなものが流れているが、基本的にプリレンダーではなく、luaなどによって実装されたものっぽい
    • 映像を作る人にも見てもらいたい 是非とも
    • 視認性としてもむちゃくちゃすぎる
  • プレイヤーが凄すぎる
    • どうやら、この↑意味のわからん譜面を初見でやるような集団らしい
    • 今回やっているものも、初見でこういう譜面をやる大会で出されていたものらしい
    • このプレイヤーも譜面を全暗記しているわけではなくてある程度以上は認識しているらしい、、、、
    • DDRでいう14あたりの物量、しかもこんな譜面をなんと本番で繋いでいく、、、、、、、、、
  • 今回で一番好きなコーナーだったと思う 僕の心を持っていった

www.youtube.com

https://www.twitch.tv/videos/1264029620?collection=EDmMkO9FyhY5Vg

CASIO MARIO WORLD

  • 僕の中の、今回の本命
  • スーパーマリオワールドのハックロム
    • SMWCで配布されているもの
    • いわゆるkaizo*1ハックの中で一番難しいExtremeにあたる作品
  • 質がかなり良いハック
    • ステージ
      • バグがほとんどパッチされていて、そういう知識を必要とするステージはない
      • 発想がかなり良い
        • けっこうオリジナルなものが多い
    • 音楽
      • Oneohtrix Point Never あたりの曲をこれのためにポートしてきている
      • 他のハックではもっぱら他のゲームの曲が使われるが、こういう方向性もアリだなぁ
  • 僕はこれをjuzcookがやっている実況動画を見ていて、そして自分でも1面だけクリアしていた
    • 1面は5時間かけてクリアした
    • それ以降はもう無理だった…
  • このハックは決して40分だとかでクリアできるものではない
    • こんな悠長に説明して38分でクリアって、、、

www.youtube.com

https://www.twitch.tv/videos/1264893926?collection=EDmMkO9FyhY5Vg

テトリスグランドマスター

  • 僕も大好きテトリスグランドマスター、の1の20GモードのGM争奪戦
  • GMを取ったのは全体のうち2人だけだった
  • SPCさんってSwitchpalacecornerだったんだ
    • SMWのTASerという印象が強い
    • でも確かにチャンネルにはTGMの動画をあげていたな
    • たしかMasterjunもTGMのTASを作ってたな、SMWのTASerの間で流行っているのかも

www.youtube.com

https://www.twitch.tv/videos/1265133498?collection=EDmMkO9FyhY5Vg

イベント全体に対する

  • 毎年そうだが、寄付の勢いがやべえ
    • 視聴者数的にはピークでも同じか倍に到達しないくらいの規模なのに、最終的には344万ドルも集めていた
    • 正直RiJは比べ物にならないな
  • 走者情報として性自認を明記するやつって前からやってたっけ
    • 日本のインターネットにありがちな有耶無耶な巻き方をせず、正面と立ち向かっている感がいいね
    • gdqPride
  • 並走時、Twitchのidとかtwitterのidとか名前とかの表示がすべて同じタイミングで切り替わっていて良い
    • というかRiJが良くないのでは
    • このあたりの完成度はやっぱり歴もあって流石だなといったところ
  • プレイヤーも解説もテンションが高めだな
    • 英語だからそう聞こえるだけ?

*1:ニコニコでも有名な友人マリオがYouTubeに転載される時、Kaizo Mario Worldという名前がつけられて以降、この手の死にゲー系ハックを総じてkaizoハックと呼ぶようになった。

アヒルのおもちゃとかテディーベアとかと話して解決するやつ

ラバーダッグ・デバッグやテディーベア・デバッグと名付けられているコードのデバッグの手法がある。

ja.wikipedia.org

プログラマーがラバー・ダック(アヒルちゃん)を持ち歩きアヒルちゃんに向かってコードを1行ずつ説明することによりデバッグを行うという話が由来である。

一見精神的にヤバいやつのように見られるが、Wikipediaには続けてこう書いてある。

プログラマーの多くは誰かに問題を説明した経験があり、その相手はプログラミングの知識が全くないこともあり得るが、問題を説明している過程で解決策を思いつくことがある。 目的とするコードと、実際のコードの挙動を観察して、説明することにより、その違いが明白になるのだ。

プログラミングの未経験者に対してプログラミング特有の問題を説明する行為はあまりにもプログラマー的な傲慢さの悪いところのように思うが、それはそれとして、この行為は問題解決に対して有用になることがある。

とある問題を他人に説明するには、様々な部分の言語化を行う必要がある。例えば、行動の目的、環境、それに対するアプローチ、実際に発生した事象、など、、、

さらに、これらの言語化のためにはまず今の状態を客観的に捉える必要がある。主に情報収集や、なにかの物差しと比べるような行動になる気がする。

このような段取りを踏んでいる間に自ずと問題がよく整理され、より深い理解へと繋がることで、問題が解決する事がある。「あれ?この環境に対してこのアプローチを取るには〇〇が足りてないんじゃないか?」といった具合で。

ここまでの例ではプログラマーは人間を説明の相手に使うが、Wikipediaにもあるように↓

無生物を用いることにより、プログラマーは、他人を煩わせることなく目的を達成できる。

というわけでラバー製のアヒルのおもちゃやテディーベアといったものも相談相手として使用する。合理的だ、、、さすがプログラマー

これはなにもプログラミングに限った話ではなく、この世の問題全てにおいて適用可能なデバッグ方法だと思っている。特に、ある程度までの大きさの心の問題についてはそこそこ有効だとも思っている*1。ふわふわと頭に浮かんでいる悩み事は、その問題に対する考えの整理がうまく行っていない事が間接的な原因の場合もあり、であればその整理を行うことで解決の糸口を掴める場合がある。例えば、雑談相手に愚痴をぶつけるだけで案外気が晴れたりするものだろう。それは意図しない形で無意識的に解決している場合なのかもしれない。

個人的な経験としても結構役立っている。バグを潰している時に作業通話の相手になっていた人に説明を試むことで明確な問題点を発見できたり*2ツイキャスでさいきんの悩みを喋っていたら実は簡単な解決方法があることに気づいたり*3、ブログの記事として数ヶ月間くらい問題になっていたことをそのまま書いていたら実は解決可能であることが分かったり*4……そういえば、一時期カウンセリングに通っていたときもよくこういう体験あったなあ

これは他人の例だが、よくお世話になっているDiscordサーバー内の通話チャンネルで、「最近どのサーバーでもほとんど喋ろうとしてなかった」と言う人が珍しく参加しており、さらに他の参加者に対して相談事をしていた。実際に「相談をした」というだけで本人の負担が少なくなったらしい(程度はわからんけど)。

本人はドツボにはまっているが、他の人から見たら解決方法が明確だった、という場合はそこそこあると思うが、その理由はきっとこのようなものなんだと思う。また、そのような説明を受けた時、相談した側も既に解決しているかもしれない。

言語化は案外めんどくさいことで、頭の中のコストをそれなりに消費する。その理由の大きな部分が上述してきた問題の整理にあると思う。話し相手というのは、この良いとっかかりとなってくれるのだと思う。多分、仲の良い人とはそこそこ積極的に雑談をしたほうが良いのだろう。

*1:一応断っておくと、もちろんこんなことでは解決できない問題というのもある。それは多分大きな問題なので、もうちょっと大枠から考慮したほうが良いと思う。

*2:よくある話

*3:このあいだのアニメの話

*4:このあいだのMODの話

公開できるmodを初めて作ったので大変だったところを書いておく

昨日、自作のmodについてのうすい記事を書いた。

naari.hatenablog.com

このmodを公開するに至って大変だったことをつらつらとかきます

fabricのMixinやInjectsの概念についてのドキュメントが少なすぎる

fabricにはMixinや、Injectsという概念がある。これは元々のMinecraftのコードに自分の好きな挙動を差し込むことができる。

たとえば以下のようなコードがあった場合、

class Example {
  void blah() {
    System.out.println("hogefuga");
  }
}

以下のようなコードを書くことができる。

@Mixin(Example.class)
abstract class ExampleMixin {
  @Inject(at = @At("HEAD"), method = "blah")
  void yo(ci CallbackInformation) {
    System.out.println("foobarbar");
  }
}

この状態で Example#blah() を実行すると foobarbarhogefuga がプリントされる。

また、以下のように処理のキャンセルを行うこともできる。

@Mixin(Example.class)
abstract class ExampleMixin {
  @Inject(at = @At("HEAD"), method = "blah")
  void yo(ci CallbackInformation) {
    System.out.println("foobarbar");
    ci.cancel();
  }
}

こうすることで、本来プリントされるはずの hogefuga はキャンセルされる。これは純粋にもとのメソッドの内容をすべてオーバライドするために使うこともできる上に、特定の条件下でのみバイパスする、といった処理も実現できる。

メソッドが呼ばれる際の引数を参照することもできるため、基本的にはこれを利用してmoddingを行うことになる。

検索して出る情報が少なすぎる

公式のドキュメントを含めて、純粋にこの@Mixinについての情報が少なすぎて大変に困っていた。最終的にはこの@Mixinを使っているmodのソースコードを参考にせざるを得なかった。

tutorial:mixin_injects [Fabric Wiki]

fabric-carpet/ServerPlayNetworkHandler_scarpetEventsMixin.java at af368a159d2c3c287acbd22d9a9c0f0094519c56 · gnembon/fabric-carpet · GitHub

結局実際の実装が一番参考になるのがドキュメントとしておかしい気がしていて、そのうち大きく手を入れることができそうに思った。日本語の情報もないし、もしそれが提供できたら喜ぶ人が結構いるのではないかと思う。

村人のgetOffers()を叩くだけだとダメ

元々のプランだと村人にフォーカスをあてた時にそのEntityの getOffers() を叩いて左上に表示しようかな~という気持ちだったが、そもそもこれが不充分だった。

用語: MerchantEntity について

村人などの「右クリック→画面が開いて取引開始」が実装されてるEntityはすべて MerchantEntity を継承している。

通信の流れ

プレイヤーが MerchantEntity に右クリックをした時、サーバー側に「このプレイヤーがこのEntityにinteractを行った」という趣旨のパケットを送信する。

サーバー側はこのパケットを受信した後、よしなにチェックを行い、最終的にはプレイヤーに「取引のウィンドウを開く」と「取引内容」のパケットを送信する。

この取引内容のパケットを元に MerchantEntity に最新の取引内容をセットするため、このパケットを掴まないとプレイヤーが望んだ情報を提供できない。

つまり、右クリックをしないと取引内容を取得することができない ということになる。

擬似的に右クリックする

これに対してどのような方針で進めるのが最適なのかはわからないが、今回は以下のような方針を取った。

  • フォーカスが MerchantEntity に当たった場合、サーバーにinteractを行うパケットを送信する
  • サーバー側から「取引のウィンドウを開く」のパケットが届く
    • キャンセルする
  • サーバー側から「取引内容」のパケットが届く
  • もし MerchantEntity を直接右クリックしていた場合は以上の処理をスキップする

正直筋が良い方法はわからないままだが、上述したMixinとInjectorsを使用してパケットハンドラーに直接処理を挟むことにした。

offers-hud/ReceiveTradeOfferPacket.java at main · naari3/offers-hud · GitHub

@Mixin(ClientPlayNetworkHandler.class)
abstract class ReceiveTradeOfferPacket {
    @Inject(at = @At("HEAD"), method = "onSetTradeOffers", cancellable = true)
    public void onSetTradeOffers(SetTradeOffersS2CPacket packet, CallbackInfo ci) {
        MerchantInfo.getInfo().setOffers(packet.getOffers()); // シングルトンなインスタンスに情報を流す
        if (!OffersHUD.getOpenWindow()) {
            ci.cancel();
        }
    }

    @Inject(at = @At("HEAD"), method = "onOpenScreen", cancellable = true)
    public void onOpenScreen(OpenScreenS2CPacket packet, CallbackInfo ci) {
        var type = packet.getScreenHandlerType();

        if (!OffersHUD.getOpenWindow() && type == ScreenHandlerType.MERCHANT) {
            ci.cancel();
            ClientPlayNetworking.getSender()
                    .sendPacket(new CloseHandledScreenC2SPacket(packet.getSyncId()));
        }
    }
}

このリポジトリを作ったのはおおよそ1年前だった気がするのだが、パケットハンドラーに手を出すという発想に至れたのはひとえにMinecraftのサーバーを1から作成したことによるものに思える。

画面上になにかを描画するのが難しい

これもドキュメントについての愚痴で、チュートリアル的なものが全くないためにどのような手段を踏むのが正攻法なのかが全く分かっていない。

幸いにも、今回の要件は「MerchantEntityの取引内容を描画する」という、既に同じような実装が存在するものだったのでこれを流用することでなんとかなった。

具体的には、 itemRenderer#renderInGui() を使うことでアイテムのテクスチャつきで描画をすることができる。テキストについても同じように textRenderer とその便利なメソッドが存在する。

もしもうちょっと踏み込んだレンダリングが必要になった場合、どうやって書けばよいのだろう。全くできる気がしない。

1.18になって何故か引数の順番が変わっててワロタ

Minecraftの画面上にテクスチャを描画するためには一般的に DrawableHelper.drawTexture が使われるようだが、この引数が1.17.x と 1.18.x で変更されていた。具体的には以下のように。

// 1.17
void DrawableHelper.drawTexture(MatrixStack matrices, int x, int y, int z, float u, float v, int width, int height, int textureHeight, int textureWidth);

//1.18
void DrawableHelper.drawTexture(MatrixStack matrices, int x, int y, int z, float u, float v, int width, int height, int textureWidth, int textureHeight);

こんなのに一発で気づくわけもなく、「なんで1.18では正しく描画されなくなっちゃったんだろう」とかなり困っていたが、fabricのためのDiscordサーバーで尋ねてみたところ、すぐにこの答えを返してくれた。本当にありがとうございました。

こちらから参加できるみたいです discuss | Fabric

modをCurseForgeにpublishする方法

今回、わりと使えるmodが作れたのではないかという自負があり、伴って一般的なmodの配布場所である CurseForge にも配布することにした。

CurseForge上にプロジェクトを作成する必要があった

ただ、そもそも会員登録すらしていなかったので、会員登録をし、さてどうやってmodを公開するのだろう、と3時間くらいサイト上を彷徨っていた。

  • authors のトップ画面にアクセスする
  • Start Your Project をクリックする
  • あとは流れで

ずっと Dashborad の上を見ていたが、このような新規作成系のボタンがひとつも存在しなかった。こういうのって普通ダッシュボードの上にあるんじゃないの?かなり難しい。

GitHub Actions を利用して公開する

その後、実際に実装した後はmodをプロジェクト上に公開する必要があるが、これがまた難しかった。

.jarファイルを生成した後にプロジェクト上に手動でアップロードする、ということもできるが、昨今のCI/CDの時代にそんなことをしていたら時間がもったいない。自動化するべきである。

これも人様の実装を参考にする

実際に、先程も参考として取り上げたgnembonのcarpetは GitHub Actions を使用してCurseForgeにmodの公開をしている。releaseを打つ度に発火されるようだった。

fabric-carpet/publish-release.yml at master · gnembon/fabric-carpet · GitHub

今回はこれをコピペし、自分のプロジェクトに向けてもう少しだけ調整したものを用意した。

具体的な実装はこれ → offers-hud/publish-release.yml at main · naari3/offers-hud · GitHub

コピペしました!だけだと味気がないので、もうちょっとだけ踏み込んでみる。↓

itsmeow/curseforge-upload を使用してCurseForgeに生成物をアップロードする

CurseForgeにファイルをアップロードするには、itsmeow/curseforge-upload という公開されている action を使用する。

github.com

自分の使い方は以下の通りで、action にトークンやプロジェクトの情報を渡したらあとはよしなに動いてくれる。チェンジログをreleaseのbodyに書けるのがかなり便利だと思った。

      - name: Upload to Curseforge
        uses: itsmeow/curseforge-upload@v3
        with:
          token: ${{ secrets.CF_API_TOKEN }}
          project_id: 566138
          game_endpoint: minecraft
          file_path: build/libs/${{ steps.findjar.outputs.jarname }}
          changelog_type: markdown
          changelog: ${{ github.event.release.body }}
          display_name: OffersHUD v${{ needs.Get-Properties.outputs.mod-version }} for ${{ steps.getbranchinfo.outputs.version }}
          game_versions: 7499,4458,${{ steps.getbranchinfo.outputs.curse-versions }} #Fabric,Java 8,[version (s) for the branch]
          release_type: ${{ needs.Get-Properties.outputs.release-type }}

game_version がすごく難しい

Game Versionというのは、CurseForge内で「そのプロジェクトがどのゲームのどのバージョンに対応したものか?」というのを表すためのもので、例えばこのプロジェクトだと fabricJava 8*11.181.18.1 に対応したmodなのでそれぞれを指定する必要がある。

  • fabric も Java 8 もゲームじゃなくない?と思ったけど、もうそうやって管理されているから仕方がないらしい。

これが結構曲者で、たとえば純粋に 1.18.1 という文字列を指定すると怒られる場合がある。例えば以下のように。

Publish Release · naari3/offers-hud@d02f693 · GitHub

{"errorCode":1009,"errorMessage":"Invalid game version ID: 8897 belongs to an invalid dependency."}

これは 1.18.1 という文字列に対応した Game Version が複数あることが原因で、この場合は全然知らない名前のゲームの1.18.1を指定してしまっていたらしい。

なので、正しくMinecraftの1.18.1に対応したGame Versionのidを発見し、そのidを直接指定する必要があった。

# これは Minecraft の 1.18.1 と 1.18 を指す
release-extra-curse-version = 8857,8830

さてこのidの対応表だが、どこかにドキュメントとしてまとまっているわけではなく、以下に存在するjsonから妥当なものを探し出す必要がある。

[https://minecraft.curseforge.com/api/game/versions?token=[API_TOKEN]]

このjson内で 1.18.1 を検索すると次の2つが発見できる。

{
  "id": 8897,
  "gameVersionTypeID": 1,
  "name": "1.18.1",
  "slug": "1-18-1"
},
// snip
{
  "id": 8857,
  "gameVersionTypeID": 73250,
  "name": "1.18.1",
  "slug": "1-18-1"
},

どちらがMinecraftのものか全くわからん…………………

解決方法としては、CurseForgeのmod検索ページの絞り込みで1.18.1を指定すると次のようなURLに遷移する。

https://www.curseforge.com/minecraft/modpacks?filter-game-version=2020709689%3A8857&filter-sort=4

このうち filter-game-version を確認することで解決できた。今回の場合は 8857 が正しかった。

また、これによって gameVersionTypeID: 73250Minecraft 1.18系 を指すことが分かったため、このidで検索すると Minecraft 1.18 や snapshot に相当する Game Version も発見できる。

{
  "id": 8633,
  "gameVersionTypeID": 73250,
  "name": "1.18-Snapshot",
  "slug": "1-18-snapshot"
},
// snip
{
  "id": 8830,
  "gameVersionTypeID": 73250,
  "name": "1.18",
  "slug": "1-18"
},

本当は一発でこのあたりが網羅的に確認できるのが嬉しいが、どうやらそういった対応表は存在しなさそうだった…

人のmodがオープンソースじゃなくて悲しい

とまあこんな感じで、人の実装を参考にしまくった結果このmodが完成した。

その中で、「そういえばこの処理ってあのmodで実現されていたな、どうやっているんだろう」と思い出し、そのmodのページにたどり着いたりするが、感覚でいうと半分くらいの確率でソースコードが公開されていない。

昨今の問題もあるのでこのあたりについてあまり強いことは言えないが、mod制作の参考に出来ないのはシンプルに悲しかった。もし公開状態であればPRを投げることも出来そうなのに。

おおよその場合において難読化が行われていないので、一応は JD-GUI などを使用することで確認を行える。

ただ、以下のようにMinecraftのdeobfuscatedなソースコードとのmappingは切られた状態でビルドされるため、具体的にどのクラスのどのメソッドと対応しているか?はfabricで使用されているMinecraftのdeobfuscatorである yarn と照らし合わせる必要がありそう。

むずい

Configを実装しなければならない

現状、機能のオン/オフすら出来ない。これだと他modとの競合があり、場合によってはまともに使うことができないだろう。

これについてもConfigを実現するための参考実装を探す必要があるが、どれを参考にすればよいのだろうか…

このあたりについてもチュートリアルは存在しないため、情報収集からデファクトを感じ取る必要があり、結構大変そうに思う。

ここから追記

ここ→ documentation:libraries [Fabric Wiki] に使えるライブラリ一覧がまとまっていて、それぞれ検索して良さそうなものを見繕った。

結果として、ClothConfigを使うのが良さそうに思った。どうやら過去デファクトになっていたAutoConfigも同梱しているらしい。

Classとしてプロパティを実装していくだけでConfigとmodmenuに対応したscreenを実装できる。

@Config(name = OffersHUD.MODID)
public class ModConfig implements ConfigData {
    public boolean enabled = true;
    public boolean ignoreNoProfession = true;
    public boolean suppressVillagerHeadRolling = false;
}

これとlangファイルで以下のようなスクリーンが生成される。嬉しいね

ここまで追記

まとめ

こんな感じで、全てにおいて他人の実装を参考にせざるを得ず、自分ひとりの力では実装しづらい部分が多々あることに気づけた。fabricのDiscordサーバーも存在するので、臆することなくどんどん利用していく必要があるように感じた。

*1:これ違くない??直さなきゃ…