ffmpegでコマごとに表示時間が違うアニメを作る

Posted on

ffmpegで静止画(pngとか)から動画を作る方法といえば、連番画像を使う方法が有名です:

ffmpeg -framerate 10 -i %03d.png -vf scale=784x1024 -vcodec libx264 -pix_fmt yuv420p -r 30 out.mp4

これを使うとすべての画像が同じ間隔で表示されてしまいます。上でいうと、000.pngも001.pngも002.pngも、全部0.1秒ずつ表示されます。

大半の場合はこれで困らないのですが、自作でアニメを作ったりしていると困ることがあります。たとえば、今回こんな感じの動画を友達と作りました:

首をかしげて, pixiv

このアニメは、最初のコマだけ1秒、最後のコマだけ3秒、他のコマは0.25秒表示されています。

こういうコマごとに表示時間が異なる動画を作るにはどうすればいいか、あんまり情報がなかったので備忘録的にメモって起きます。

1.jpegから5.jpegを使って上の動画を作るという設定で説明します。

githubリポジトリにデータ付きでアップロードしておいたので、よろしければそちらも参考にしてください。

タイミング情報を纏めたファイルを作る

file '1.jpeg'
duration 1
file '2.jpeg'
duration 0.25
file '3.jpeg'
duration 0.25
file '4.jpeg'
duration 0.25
file '5.jpeg'
duration 3
file '5.jpeg'

こんな感じのファイルをinput.txtみたいなファイル名で作成します。5.jpegを3秒間表示するために、最後にもう一度5.jpegを書いてる点はちょっと注意です(これを書かないと5.jpegが1フレームしか表示されません)。

適切にコマンドを実行する

ffmpeg -f concat -i input.txt -vf "scale=-1:1024" -vcodec libx264 -pix_fmt yuv420p -r 30 out.mp4

いくつか解説です:

  • -f concat を忘れないでください
  • scale=-1:1024を使うと縦1024ピクセルにした上で横幅はアスペクト比に応じて自動で決定してくれます。
  • -r 30は出力動画のフレームレートです。


「さきゅばす」の開発をやめて悪魔を祓い、苦楽を共にした彼女を追悼する

Posted on

かつてわたしと苦楽を共にし歩んできた「さきゅばす」は、いまやゾンビとなり、インターネットの地下倉庫で、苦しそうに彷徨っている。

だから、せめて…生みの親であるわたしがシャベルでその息の根を止めて埋葬し、わたしは新天地へと向かわなければならない1

さきゅばす」とは何だったのか

「さきゅばす」は、「ニコニコ動画レコーダー」である。

一言で伝えられる短いキャッチコピーを考えるのにも10年掛かり、最近やっとここに着地した。やめるけど。

えーっとね。ニコニコ動画の動画ビューワは、投稿された動画の上にコメントの文字列を再生するたびに毎回重ね描き2して表示しているんですが、その代わりに既に最初からコメントが描きこんであるような動画を生成するソフトウェアです。

こんな感じの動画が作れます:

レッツゴー陰陽師

きしめん/Nursery Rhyme

12年半ほど間欠泉のように開発してきたこのソフトウェアですが、もういい加減、開発・メンテという形で関わるのは、もうやめようと思います。

わたしは株式会社ニワンゴ・ドワンゴ・カドカワとは雇用契約以外も含めた一切の金銭的な関係を持ったことはなく、決してこれは退職エントリではありません。そもそも、ただのOSSだし。

とはいえ、ある意味では雇用契約よりもはるかに強い、「悪魔と契約純粋な趣味として」して作ったソフトウェア3ですので、強いていうなら「退魔・追悼エントリ」でしょうか。てなわけで、こんなタイトルになった。でも俺は悪魔と契約しちまった以上、死ぬまで魔女はやめねーかんな。なぜなら、悪魔との契約は生涯有効だからだ。

引き継ぎ資料

さてさて。とはいえ、「さきゅばす」をまだ必要としている人はまだいるでしょうし、開発を引き継いでくれる人がいるなら、それが一番なのは間違いありません。そんなわけで、引き継ぎ資料を作りました。

ただしおそらくこのドキュメントを見てコマンドを打つだけでは、ビルドと実行まではできても、最後まで動画を変換しきることはできず、いくつかの箇所でエラーでコケると思います。(引き継ぎたいあなたへ:悪魔と契約するんだ、デバッグとソースコードの修正をする覚悟ぐらいは持っておくれ)

さて以下では、こんな感じで10年間降り積もった同窓会的なよもやま話を延々といたします。どんな気持ちで作ってたのか知りたかったら、読んでみてね。長いよ。

どうしてやめるのか?

他にやりたいことがあるから

やりたい事・作りたいものが、たくさんあるからです。

死ぬまでに作りたいもの、上手になりたいこと、知りたいこと、研究したいこと、描きたいこと、記したいこと、見たいもの、…そんなものが両手で数え切れないほど、たくさんあります。でも、人間には寿命があり、いつかは死んでしまいます。すべてを行うことも、見聞きすることも出来ません。最近ついにそこに気づきました。

そうして天秤に掛けた時、「さきゅばす」からは、…「彼女」からは、もう、知りたいことは全部教わったし、作りたいものは全部作ったし、描きたいものは全部描いたし、研究したいことは研究しつくしたし、見たいものはもう全部見せてもらいました。

…つまるところ、さきゅばすでやりたいことは、わたしには、もう残っていないのかなぁって。

ニコニコ動画を見ていない

ニコニコ動画を(古いお気に入りの動画をふとたまに見たくなった時以外)全く見ていない事に気がつきました。すこし昔は「アニメを見る時ぐらいはニコニコ動画」だったような気がするのですが、最近はそれもやめ、アニメを見る時は1人でじっくり鑑賞し、噛みしめるように見ています。

そんなわけで、ニコニコ動画を使っていないわたしには、ニコニコ動画の作りやすいツールは、流石にもうこれ以上作れないと思いました。…せいぜい動き続けるように、メンテできるだけ。

でもそれって、「ゾンビ」そのものじゃないですか。わたしは、そんな彼女の姿を見たくはありません。

必要性が薄まった

さきゅばすは元々は100万回もダウンロードされるために作ったソフトウェアではなく、2007年6月当時の個人的な欲望と興味と関心を具現化するため、高校にもロクに行かずに引きこもりながら実験的に作ったソフトウェアです:

  • PSPでニコニコ動画が見たかった
    (PSPにはニコニコ動画アプリのようなものが無かった)
  • 2007年当時の環境では、弾幕4が重く、PCの性能不足でプレイヤーがコマ落ちしてしまった

でも、2019年現在では、スマホアプリも、各種ゲーム機向けのプレイヤーもあります(端末別ヘルプ、PSP向けはついぞ出なかったようですが)。お気入りの端末でニコニコ動画が見られない、なんて事は、もう無いと思います。変換した動画ではなく、各アプリなら、コメントを見る見るだけでなく投稿することだってできますし、他の動画の検索だってできます5

性能面に関しても、2019年のスマートフォンでも十二分すぎるほどの性能があります。実はスマホをもう持ってないので分かりかねるのですが、「コメントがたくさん出たら画面がカタカタになった」なんて事態は、おそらく流石にもう無いんじゃないでしょうか。少なくとも聞いたことない。

投稿されたコメントアートを残したい、という需要があるということが、orzさんやそれを通じて知り合ったコミュニティを通してわかったのですが、2019年現在では、2019年に投稿されたコメントアートを保存する最善の方法は、おそらく単に録画する事のような気がします。もっと言うと、2019年のパソコンには、それをやるだけの十分な計算資源があります(逆に言うと、2007年当時は、再生された動画をリアルタイムに録画するのはとーっても大変でした)。もちろん、過去に投稿されたコメントアートを今ここで再生・保存したいなら、当時の挙動を再現するコードを書くしかなく、そこに「彼女」の居場所が生まれうる、とは思います。

デフォルトでは動画全体を通して最大1000コメントしか表示されないところを、無理やり数万コメントを突っ込んでコメントで溢れさせる弾幕動画を作るのは…もういいかな。やりたい人が居たら、わたしの代わりに、彼女を召喚ビルドしてお願いしておくんなし。

うーん。

こうして箇条書きにして当時の「モチベーション」とやらを書き出してみたのですが、いくら書いても、ここからこぼれてしまう部分があるような気がします。人はそれを、「情熱」とか「熱意」と呼ぶのでしょうか…。よくわかりません。

…なんだろ、tcpdumpでdumpしたパケット眺めてさ、ActionScriptを逆コンパイルしてさ、ffmpegのソースコード読んで書き換えてコンパイル通してさ、ニコニコ動画の弾幕コメントを表示させるのは、仮に何の意味もなくても、確かにどうしようもなく楽しかったよ。

誰も認めてくれなくても、それがわたしの青春の1ページだ。

さきゅばすの作り方

「資料なんかどこにもないのに、どうやって作ったんですか?」と以前聞かれた事があるので、文章にして残しておきます。

WebAPIの叩き方→tcpdump

ニコニコ動画のサーバから、動画とコメントをダウンロードする方法は、単にブラウザの挙動を追いかけて推理しただけです。ただし、F12を押すと出てくる「開発者ツール」のような便利なものなど存在しない時代だったので6、tcpdumpを使って、ニコニコ動画のサーバと自宅のブラウザが交換するパケットストリームを眺めて観察しました。観察した結果を元にAPIの使い方(リクエストを投げる時のXMLの書き方とか)の仮説をたて、それを確かめるためには、時にはtelnet nicovideo.jp 80ってやって直接手でHTTP/1.1のリクエストを投げて確かめてた気がします。今ではニコニコ動画もHTTPSに対応してしまい、通信はすべて暗号化されてしまったので、今では絶対できない方法ですね…。懐かしや。

コメントの配置方法→逆コンパイル

コメントの配置の仕方は、FlashのプレイヤーのActionScriptを逆コンパイルして、それをそのまま実装しました。今思うと、ちょっとヤバかったかも。でもそのおかげでそこそこ再現性は高かったんですよ。逆コンパイルすると変数名とかが全部「var1」とかになったソースコードが現れてきて、一般に解読はなかなか大変なのですが、当時はゲームのコピープロテクト外しも夢中だったので、「アセンブリじゃなくて、ちゃんとif文やfor文が出てくる!なんてわかりやすいんだ!逆コンパイラってすげー!」みたいな感じでした。…いまやれって言われたら、できるかなぁ。

コメントのレンダリング→適当 or 逆アセンブル

コメントの描画の仕方は、最初はSDL_ttfを使って適当にMSゴシック使って描いていたのですが、後にFlashそのものを逆アセンブル・解析して、ExtTextOutWというWin32APIを特定のパラメータで叩いてることを突き止め、cairoというライブラリでなるべく同じようにWin32APIが叩かれるように実装しました7。これも、今思うとちょっとヤバかったかな。ただ、この解析結果が実装されているのは、ゼロから書き直したSaccubus2だけで、引き継ぎ資料でビルドしてるSaccubus3は初期のバージョンである1がベースなので実装されていません。やりたかったらやってみてね。

ffmpegの改造方法→素直にソース読む

ffmpegの改造の仕方は…普通に変換してるところのソース読んで追いかけて、その結果をもとに、さきゅばす用にちょびっと改造しただけです。話を整理してみると、実はここが一番簡単かつ素直だったことが分かります。ただ、変化の激しいffmpegを改造して維持つづけるのは中々大変でした。この話はまた後でします。

アーキテクチャ図

ちなみに、全体のアーキテクチャはこんな感じです:

この図にして見ると結構複雑なアーキテクチャはどう思いついたのか?…うーん、思い出せません。でも結構素直ではあると思うんですが、みなさんはどう思います?

さきゅばすの思い出

ここからはさらに個人的な思い出話です。ガンガン回顧するぜ。

自宅サーバから配布しすぎてISPからアカウントを永久凍結された回

さきゅばすを開発した当初、このウェブサイトは自宅サーバの上でホスティングされていました。なので、開発したソフトもその自宅サーバから配信してました(当時のブログ記事)。

公開してしばらく経ったある日、とある「個人ニュースサイト(これも懐かしい響きだ)」で取り上げてもらって突発的にすごい人気が出て(今で言うとバズって)一週間で数万回とかダウンロードされるようになりました。

…それはそれでとーっても嬉しい話なのですが、5MBぐらいあるソフトウェアが1万回ダウンロードされると、50GBのアップロード・トラフィックが発生します。当初使っていたプロバイダは(安い代わりに)月々5GBまでしかアップロード・トラフィックを出してはならないという規約になってまして、そのうち見事にアカウントが凍結されてインターネットに繋げなくなり、ウェブサイトも公開できなくなってしまいました。そしてアップロード・トラフィックなんか気に掛けた事無かったので、理由もわからないままある日突然繋げなくなってすごい焦った…(そしてプロバイダからの紙のお手紙が後に届いた)。

当時は「なぜアップロードが…?」と思っていましたが、10年後ぐらいにデータセンターの運営をやるようになって納得しました。インターネットでは、電話と同じで、アップロードする側がお金を払う

つまりISPは、ユーザーがアップロードすればするほど損をするんですね。昔、ISPがP2Pを嫌がって規制したりしてたのも、結局最終的にはそこ(アップロードの増加による経済的損失)だったのか、とかも芋づる式に完全に理解しました。

毎月パソコン雑誌がなぜか家に届く回

人気が出た結果、複数のパソコン雑誌に定期的に収録してもらえるようになりました。収録されるとソフトの開発者は無料で献本してもらえるという習わしがあったので、ほぼ毎月のように様々なパソコン雑誌が届いてた時期がありました。しかし毎月毎月同じソフトが別の雑誌とはいえ収録されるというのも、今思うと不思議な話だ…。

Flash黄金期である2000年代前半に「ぶっこ抜き」という標語と共に生きていた世代だったので、「ネトラン」に収録されたときはちょっとした謎の達成感(?)はありました。

…何もかもが、懐かしい。

ユーザーは説明を読んでくれない

「さきゅばす」は上にも書いた通り「実験」として作ったソフトウェアでしたので、当初のUIは限りなく適当でした。どれぐらい適当かというと、文字を描くためのフォントのパスを自分で打ち込まないと変換できないぐらい、適当なUIのソフトだったのです。

そんなソフトですから、人気が出ると、すぐに「変換できません」という投稿でコメント欄があふれてしまいました。対応策は分かっていて、WindowsのMSゴシックのパスをデフォルトでハードコードしておけば、ぶっちゃけ大半のケースでは困りません。

が、せっかくJavaで書いたのにそれで乗り切るのは負けな気がして8、かといってWindows/Mac OSX/Linuxのすべてで適当なフォントを探すためのJavaコードを書くほどの気力もなく9、「Windowsの人は次にあるMSゴシックのフォントを打つか、フリーのフォントをダウンロードしてそのパスを入力してください」とダウンロードリンクのすぐ上に赤字でデカデカと書くという、今になって冷静に考えると一番ダサい解法で乗り切ろうとしたのですが、デカデカと書いたのにも関わらず、「動きません」というコメントの勢いが衰えることは一切ありませんでした。読んでくれないんだね。町中でダサいテプラを見るたびに、いつもこの事を思い出して、テプラ貼った人に心の中で同情してしまいます。

結局、最終的には腹をくくってWindowsの時はWindowsのシステムフォルダの中にあるMSゴシックのフォントのパスを発見してくるコードを書きました。

とにかく自動でとりあえず大半のケースでは動くようにすることがすごい大事で、人はとにかくログは読まないし、エラーメッセージも読まないし、ドキュメントも読まないし、でもコミュニケーションはする、という、人間へ対するある種の諦観を、このとき手にいれました。この諦観は、この後、他のフリーソフトを書いたり、仕事としてソフトを開発・運用をしたりする中で、何度も何度も強化されていったような気がします。

特定のCPUでクラッシュする

一時期、Crusoeの載ってるVAIO(まだSONYが作ってた頃!)を併用していたのですが10 、さきゅばすはこのPCでだけ、ときたまクラッシュすることに気づきました。トラブルシュートした結果、最終的にSDL_ttfのgccのコンパイルオプションでSSE2かなにかを無効にすると再現しなくなったので、おそらくSIMD命令の互換性がCrusoeでは怪しいのだろう、という結論に。

ただそれだけなんですが、なんというか、パソコンの裏側の、そのまた裏側を覗いてしまったような…見てはいけないものを見てしまったような…何か隠れていたものを掘り起こしてしまったような、そんな気持ちになったエピソードです。

cygwinとmingwの思い出

Windows上でPOSIX互換のプログラムを書くためのソフトウェアツールキットとして、MinGWcygwinがあります。前述したように互換性厨だったので、C言語で書いてあるコメントを描画する部分については、Win32APIを叩くのではなく、こちらを叩いて実装しました。MinGWだとpthreadが無いとかで(詳細は忘れた)、もっと楽なcygwinで当初は開発・配布してました。

cygwinを使ってコンパイルしたバイナリは、cygwin-1.dllに必ず依存するようになります。それ自体は別に問題ではなく、一緒にcygwin-1.dllを配布すればよい…のですが、ただ、他のソフトと同時で起動した時に、そのソフトが別のバージョンのcygwin-1.dllに依存していたりすると、異なるバージョン間で何かがぶつかりあってクラッシュしてしまうらしく、定期的に「動かないのですが」のコメントが来ていたので、頑張ってMinGWで動くように改修した記憶があります。もっというと出来る限り全部をstatic linkするようになっていった記憶が…あります…。

ソフトウェアのバイナリを配布して動かしてもらう場合、相手の環境は前もっては分からないので、かなり想定外な…ともすれば理不尽なエラーに出くわしたりします。…スマホの時代になっても、そのへんはあんまり変わらないかもしれませんねぇ。

ffmpegを改造しながら4〜5年毎日Jenkinsでビルドした

ffmpegの改造自体は結構簡単だったのですが、ffmpegは頻繁にアップデートされ、内部構造も結構大胆に変わります11。それに毎日追従して改造しつづけながら何年間も毎日Jenkinsでビルドし続ける、というのをしてました。ぶっちゃけ、この作業が一番大変だったな。

今思うと、別にそこまでする必要無いよな〜。でもDaily buildとかContinuous Integrationって文字列が、なんか、こう、かっこよくて…やってみたくてさ…。

コメント描画フィルタを実装する3つの方法

最初は、コメントを描画するために「vhook」という、画像から画像に変換するフィルタをDLLで注入できる機能を使っていたのですが、これはすぐになくなりました12

しょうがないので、avfilterでvhook相当の機能を足すvhextというfilterを書いてffmpegのソースに入れました。使うためのドキュメントはあっても作るためのドキュメントがなくてよく分からず、とにかくソースを読みながら手探りで入れたような記憶があります。

が、libavfilterを改造する方法だとかなり頻繁にコミットがコンフリクトしてその度に解決するのが辛かったので、Saccubus2ではまた方法を変え、libavdeviceにsaccubusという名前の仮想入力デバイスを作って、その仮想デバイスが動画のデコードとコメントの描画をした上で、後段のffmpegのエンコーダに渡す、という実装に変えました。

仮想デバイスの追加先であるlibavdeviceはそんなに頻繁に更新されないようで、あんまりconflictに悩まされなくなったような記憶があります。

さらに仮想入力デバイスにしたおかげで、動画のfpsを自在に変えられるようになったり、結構柔軟性は上がりました。ffmpegのAPIたくさん叩かないといけないから、大変だったけどね。

動画の長さをDLLへ渡すための、いくつかの方法

そうそう、コメント描画部分にわたさなければならない情報として、動画の画像とその時間やコメントそのもの以外に、「動画全体の長さ(秒数)」があります。

というのも、ニコニコ動画のコメントは投稿されたタイミングの1秒前から3秒間画面を流れるのですが、その3秒後が動画終了より後だった場合、コメントの描画開始タイミングを前倒しして必ず3秒間表示するようになっています13

この挙動を再現するには、コメントを描画しはじめる前に動画の長さを知っておかなければなりません。

そのために、当初はffmpeg.cの中で動画の長さが取得できるようになったタイミングの箇所を見つけて、そこに動画の長さを後で使うために記録しておくコードを挿入していた記憶があるのですが、ffmpeg.cはあんまりにも書き換えられまくってコミットがコンフリクトしまくるので、結局諦めて変換のためにffmpegを叩く前にffprobeコマンドを使って長さを調査する実装に変えたような…。

「ffmpegのプロセスを実行する回数はできるだけ減らしたい」みたいな、(今思うと無意味かもしれない)妙なこだわりがありました。その後に作ったSaccubus2でも仮想入力デバイスが長さを取得・計算していて、やっぱりffmpegのプロセスは変換ごとに一回しか起動しないようになってます。

追従し続けるにはmergeではなくrebaseするしかなかった

途中からgitを使ってffmpegの改造ソースも管理するようになったのですが(それまでは…どうしてたっけ…)、forkしたさきゅばす用ffmpegのリポジトリを本家に追従させるために、mergeするのではなく、定期的に毎回本家のmasterブランチからrebaseするという方法に落ち着きました。

「rebase絶対許さないマン」がいるのは知ってるし、その理由も頷けるんですが、1年に数千〜万コミットされるソフトにパッチをちょこっとだけ当てるというケースでは、rebaseにしておかないと、自分たちが加えた変更のコミットは大量のコミットログの中に埋もれていき、conflictを解消するだけのmergeコミットが数千コミットに一回ずつ現れるようになり、コミットIDも段々とぐちゃぐちゃになり、最終的に訳がわからなくなったので…。

歴史の管理をする、メタ・歴史管理ソフトがほしいです。

ニワン語の実装

わたしにとっての、初めてのプログラミング言語処理系の実装。それがニワン語の互換処理系(インタプリタ)である「ねこまた」でした。

ニワン語のsyntaxとsemanticsをまとめた不思議なサイトがあったので、だいたいそのとおりにC++でパーサとインタプリタを実装して、細かい部分に関しては、ニワン語で書かれた音ゲーを実際に動かしながら「文法エラーにならないように」syntaxを調整し、「実行した見た目が同じ挙動になるように」semanticsを調整していく、という感じで細部を詰めていきました。音ゲーが最後まで動いた時は嬉しかったな。

ココロの肩書きは一生有効です」と昔CPUの本の人は書きましたが、「初めて書いた本格的なインタプリタが、仕様書もなければ処理系のソースもないプログラミング言語の互換処理系であり、ゲームがちゃんと一本動いて遊べた」という、中々レアな肩書き(?)は、わたしのココロの中で、誰にも理解してもらえないかもしれないけれど、それ故に誰からも曇らせることができない、きれいで澄んだ輝きを、いまでも放っています。ええ、この文章を書いている瞬間もです。

ニワン語は、未踏で作ったシステムの一部であるプログラミング言語「ど〜なっつ」のアイデアの原型にもなっていますし、それは更に、今友達と書いてる小説と、そのゲームにもつながっています。

間違いなく、ニワン語とそのアイデアは、わたしの血肉の一部です。

そういえば元々のニワン語を作った人もなかなかのカワリモノで、結局ドワンゴを辞めてしまったと聞いたのですが、今はなにをしてるんだろう。一回一緒にご飯を食べながらお話したいなぁ。もしもこれ読んでたらTwitterでもMastdonでもメールでもこのコメント欄でもなんでもよいので、連絡してくだされ、頼む。

追悼の一環として、動いた音ゲーのTAS動画(フルコンプ)を再掲します:

このゲームの実装されていた元動画を今見たら、ふつうのPVに戻ってしまっていました。もちろん、もうニワン語は公式にサポートしなくなったのでそれが正しい挙動…なのですが。諸行無常。

このゲームだけでなく、他のニワン語を使った動画のプログラムも動いてました:

こっちも今みたら、普通の動画になってしまった…寂しい。

ニコニコ学会βとニコニコ超会議

そんな感じでだらだらソフトを書いていたところ、ニコニコ学会βというところで喋らないか、というお話を頂いて、2回(第二回最終回発表させていただきました。最初はさきゅばすの簡単な紹介とver2.0の自慢(笑)、次は、その後の近況報告みたいな感じかな。

…かわいいお洋服を着て好きなことが喋れたので、余は満足じゃ。

今では在宅勤務して働いてるぐらいには根っからの「ひきこもり」なんですが、大勢の前では意外と喋れるなぁと思って、そこが面白かったかも。

orzさんの貢献とコメントアート

途中から、orzさんという方がコミットしてくれるようになりました。なかなかシャイな人でどんな方かは分からないまま、いつのまにか見なくなってしまったのですが…お元気ですか。

orzさんが貢献してくれたのは主にコメントアートへの対応で、ソースコードを読んでバイナリを解析してるだけでは到底できない、コメントアートコミュニティの知見(例えばこういう知見)を踏まえた改修(や、ニコニコ動画のWeb APIの変更への追従)をしていただけました。ニコニコ学会βやorzさんを通じてコメントアートコミュニティの方とも知り合いになったり…ほんと、フリーソフト開発って何があるかわからないです。

ただ、正直に言うと、最終的にコメントアートはどうしても理解しきれませんでした。色々な意味で。ニコニコ動画への興味が失われかけてた頃に知ったからかなぁ。とはいえ、こういう事に真剣に取り組んで表現・実践・議論している人たちが眼の前にいるんだなぁ、というところを目撃できたのは、いい経験だったと思います。

ところでなんで「さきゅばす」なんですか

これも、

  • 「そもそも「さきゅばす」ってなんですか」

って聞かれるパターンと、

の2パターンがあります。ソシャゲの流行に伴ってえっちなサキュバスの知名度がむちゃくちゃ上がったので、近年は後者のパターンが増えました。10年以上も変わった名前のソフトを作ってると、そういうこともあります。

ではなぜ「さきゅばす」なのか。辞めるわけだし、そろそろ文章として残しておいても良い頃でしょう。

初期のバージョンを作ってた時(2007〜2008年ごろ)に、ニコニコ動画で違法にアップロードされた「ひぐらしのなく頃に」と「涼宮ハルヒの憂鬱」のコスプレAV14を見るのがお友達の間で流行っていて、その時にひらめいた連想ゲームです:

味のある演技15のコスプレAV→「えっち(?)だけど、なんかこわくて、夢の中に出てきそうな、おんなのひと」→サキュバス→「さきゅばす16」。

…そういえば気がつくとニコ動はホモビデオばっかになってノンケは完全に閉め出されてしまったな。あくしろよ。おっそうだな?

ちなみにアプリのアイコンがポケモンのムウマだった17のは、サキュバスは夢魔で、ムウマも夢魔だからです。

こういう連想ゲームは、今でもずっとやってますね。働いてる会社で流石にやりすぎって戒められた事もあるぞい。人間、そうそう変わるものではない。

最後に

これを書いている間、ずっとsm9「レッツゴー陰陽師」を聞いていました。当時は「電波ソング」とゲラゲラ笑いながら聞いていたのですが18、今聞いてみると…結構いい曲だなって思っちゃった。となると、このブログ記事も「電波ゆんゆん」でしょうか?

というわけで、最後に「レッツゴー陰陽師」の気に入ってる一節を引用して終わります。矢部のピコ麻呂に「さきゅばす」も成仏させてもらうか、そこまでは許してもらうか、わたしの中ではまだ結論はついておりません。

辛い時、悲しい時、人はそんな時心の隙間に闇が出来る。
その心の闇に”魔物”達は容赦なく入り込んでくるのだ。
だから、苦しくても、くじけるな。落ち込むな。くよくよするな。
何事にも屈しない強じんな心こそが、最強の武器なのだから。

カドカワ、最近は元気がないようですが頑張って。陰ながら、応援しています。

  1. 元ネタ: https://gakkougurashi.com/ []
  2. 初期はFlash、今はWebはPixi.JS、スマホやゲーム機は、またそれぞれ別の技術 []
  3. あとで書きますが、サキュバスは悪魔の名前です []
  4. たくさんのコメントが一画面上に同時に表示されること []
  5. 「あえて出来ないのがよい」という価値観も、わたしの中に無いではないんですが []
  6. JavaScriptのデバッグのためにalert()使ってた時代といえば伝わるかな []
  7. どうしてWin32APIを直接叩かなかったのかはちょっと思い出せない []
  8. 当時は移植性厨だったので []
  9. そもそもmac持ってなかったし []
  10. バッグに入れてイヤホンつないで音楽聞きながらちゃりんこ乗ってたぜ。スマホで聞きながら走ってる連中より10年早くな! []
  11. libavというフォークもこの間に生まれた…まだ仲直りしてないのかお前ら []
  12. 最初のリリース時点ですでにdeprecatedだったのでしょうがない []
  13. なので最後の3秒だけは特にコメントの密度が高い []
  14. 流石にR18のところはカットされてた気がする []
  15. 穏やかな表現 []
  16. 英語表記のsaccubusも、普通の表記のsuccubusからちょこっと捻ってます []
  17. 今思うと著作権的に完全にアウト []
  18. この言い回し、最近聞かなくなりましたね []

俺もココアだ―「ご注文はうさぎですか?? Wonderful party!」

Posted on

今日一週クリアしたんですが、エンディングの日はちょうどクリスマスの朝だったので、スケッチするように感想をさっと書き留めておきたいと思います。

ネット上ではこの画像で(一部で)有名なゲームです:

ですが、このゲームは実はココアさん「だけになる」わけではないです。いわゆる「個別ルート」に入って以降は、あなたはチノちゃん、リゼちゃん、千夜ちゃん、シャロちゃんのそれぞれに「なります」。いいか、なるんだよ

原作と同様、物語に大きな起伏があるわけではありません。異世界チートもないし生死を分ける戦いもないです。

前半では、あなたはココアさんとなってチノちゃんの誕生日を最高のものにすべくバイトとチラシ配りを一生懸命がんばって誕生日パーティを開き、
後半では、(チノちゃんルートでは)あなたはチノちゃんとなって、誕生日のお返しとして、たのしいクリスマスパーティを開くべく、人付き合いの苦手な彼女なりに奔走する。

それだけです。あえて悪く言うと、淡々としてます。人によっては退屈だと思うかもしれないくらい。

ですが、それでいい。

「グッドエンド」と呼ばれているものも、いわゆる普通の美少女ゲームと比べたらなんてことないです。「日常のささやかな一コマ」の範疇に収まってしまうかもしれません。でもそれでいいんです。なんでそれでいいのかわかりませんが、それでいい。

物語は、ココアさんがチノちゃんの誕生日(12月4日)が数週間後に近づいていることを知るところから始まります。

そしてココアお姉ちゃんは決心します。今年のチノちゃんの誕生日を、最高のものにしよう、と。

そのために、バイトを頑張って、あとコスプレしてチラシ配りをして、ラビットハウスの知名度をあげて喜んでもらおう、と。

…かなり発想が飛躍しています(たぶんシステムが決まってからシナリオが逆算されたんだと思う)。が、物語の中で「あれ?なんでチラシ配りしてたんだっけ??」ってココアさんも忘れる描写があるのでOKです。これを書いてて、わたしもなんでチノちゃんの誕生日のためにチラシ配りしてたのか忘れました。俺もココアだ

バイトのパートは正直難しいです。たくさんのオーダーにワーキングメモリが押しつぶされそうになります。でも、それもチノちゃんの誕生日を最高のものにするためです。バイトがうまくいってラビットハウスの知名度が上がるとなぜかうれしいのです。

チノちゃんの誕生日までに特定のキャラクターとの親密度をあげるか、ラビットハウスの知名度を最大にあげないと、(チノちゃんは喜んではくれるのですが)そこで物語は終わります。ここで「もっとチノちゃんに喜んでもらえる、最高の誕生日にできたはずなのに」という、悔しさにも似た感情が発生します。チノちゃんにモテなくて悔しいんじゃない、チノちゃんの誕生日を最高のものにしてあげられなかったことが悔しいんだ。これでは俺はお姉ちゃん失格だ。

チノちゃんの誕生日を最高のものにすることができたら、そのお返しとしてのクリスマスパーティを準備する、第二の物語が続きます。
この時は、(チノちゃんルートの時は)わたしはチノちゃんになります。

システム自体はいわゆる普通のギャルゲーで、三択したときに正解だと「友情度」が上がってエンディングが分かれるやつです。

でもそんなのはどうでもいい。チノちゃんになれば、自ずと選ぶべき選択肢はわかります。少なくともそんな気持ちになります。ほんとです。

あんなに素敵な誕生日を用意してくれたココアさんにお返しがしたい。でもわたしは、みんなでわいわいするパーティなんて開いたことないし、そもそもクリスマスといえばもっと慎ましく過ごすものだと思ってた。でも、そんなクリスマスじゃ、きっとみんな退屈しちゃう…どうしよう…。

チノちゃんは人に頼るのが苦手です。ですから、相談するのもいちいち憚ってしまう。ココアさんは相談してねと重ね重ね言ってくれるけれど、ココアさんにお返ししたいのだから、ココアさんには頼れません。最終手段です。

ラビットハウスはクリスマスももちろん営業があります。だから、一日貸し切って、なんてこともできません。仕事の後にパーティを開くしかない。そこがまたリアルなんです。

そんな彼女が出した「クリスマスパーティ」の答えは何か?これはもちろん遊んで確かめてほしいのですが、ココアさんのようなみんなを巻き込んだ盛大なものでは決してない、彼女なりのささやかな、でも一生懸命な素敵な答えです。


パジャマパーティーたのしそう

前半パートでは、毎週末集まってパジャマパーティがあります。これがまた楽しい。

そしてわいわい遊んだ後は、ココアさんとほかの誰かで一緒の布団で寝ます。添い寝しながら楽しく喋って寝る、それだけです。エロゲーは理解できなかったわたしでも、これなら理解できる。「ココアさんになれ」ました。楽しい。

「2人で一緒に寝たら残りの3人はどうしてるんだろう」と思わないではないが、そこは…きっと描かれてない3人の物語があるに違いない。


俯瞰してみると、「モバ美ちゃんPV」と同じく、「美少女 ⇔ 消費するオタク」みたいな構図を極力避けるようなシナリオとシステムになっています。ココアさん「だけ」になるんではなく、後半はほかのキャラクターにもなるので、一切片方向の視点で終わることがありません。ココアさんだけの視点でゲームが最初から最後まで完結していたら、きっとココアさんが実質的に「モテる主人公」になってしまっていたかもしれません。でも、このゲームでは、後半ではココアさんのために奔走することになります。ここがいやな感じが一切しない。


LinuxでvkEnumerateInstanceLayerPropertiesが何も返さないのをなんとかした

Posted on

ちなみに環境はUbuntu 19.04 + Radeon RX 580です。

OpenGLに疲れた

最近OpenGLに疲れはて、Vulkanに乗り換えようかと思っています。

  • メインスレッドからしか叩け無い制限。
  • bindというグローバル変数より邪悪な概念。
  • 関数を呼び出した後にglGetError()を毎回叩かなけばならない苦痛(叩き忘れるといつエラーになったのか、わからん)。
  • シェーダーへ変数へ渡すためのあの無限の関数群。
  • OpenGLのバージョンによって今では利用が必須だったり、逆に使えなかったりする「拡張機能」。
  • IndexBufferObjectだのElementArrayBufferだのVertexArrayObjectという意味不明な命名(これ…何だと思う?)。
  • 同じ関数でもOpenGLバージョンによってたまに意味が違ったりするあのややこしさ。
  • ググって出てきた情報の「今では使えなさ」(だいたいそのままコピペしても動かない)
  • GLSLのバージョンごとの気まぐれさ。
  • 結局何が起こるのかよくわからん関数のドキュメント。
  • Core ProfileだのCompat Profileだの

…ぼくもう疲れたよパトラッシュ。

そんな感じで最近Vulkanでなんとかする方向でやってこうと思ってまして、で最近引っかかった問題の解放を備忘録代わりにメモっておきます。

VulkanにはLayerなるものがあるらしい

Vulkanではある種の拡張機能を「Layer」といいます1

たとえば、標準ではこんなLayerがあるんだってさ(LunarGのサイトから引用。CC=BY=ND):

Layer Name Layer Type Description
VK_LAYER_LUNARG_api_dump utility print API calls and their parameters and values
VK_LAYER_LUNARG_assistant_layer utility highlight potential application issues that are not specifically prohibited by the Vulkan spec, but which can still create problems
VK_LAYER_KHRONOS_validation validation the main, comprehensive Khronos validation layer — this layer encompasses the entire functionality of the deprecated layers listed below, and supercedes them. As the other layers are deprecated this layer should be used for all validation going forward.
VK_LAYER_LUNARG_device_simulation utility allows modification of an actual device’s reported features, limits, and capabilities
VK_LAYER_LUNARG_monitor utility outputs the frames-per-second of the target application in the applications title bar
VK_LAYER_LUNARG_screenshot utility outputs specified frames to an image file as they are presented

VK_LAYER_KHRONOS_validation、便利そうだなー使いたいなー。

環境によってはこれ以外のLayerも使えるらしく、実際にどんなレイヤーが使えるか列挙して調べるためのvkEnumerateInstanceLayerPropertiesという関数があります。

で、これを使って実際に列挙してみました。

std::vector<VkLayerProperties> vk::enumerateInstanceLayerProperties() {
  uint32_t numProps = 0;
  {
    VkResult const result = vkEnumerateInstanceLayerProperties(&numProps, nullptr);
    if(result != VK_SUCCESS) {
      return std::vector<VkLayerProperties>();
    }
  }
  std::vector<VkLayerProperties> props;
  props.resize(numProps);
  VkResult const result = vkEnumerateInstanceLayerProperties(&numProps, props.data());
  if(result != VK_SUCCESS) {
    return std::vector<VkLayerProperties>();
  }
  return std::move(props);
}

が、これを動かしても空配列しか返ってこない。エラーではなく、成功した上で0個です。たぶん呼ぶタイミングを間違ったりはしてない。

うーん、おかしい。さっきの6つのレイヤーは、Vulkanの仕様上必ず入っているはずなのです。

ためしにググって見た所、こんな感じだったので、みんな困ってんだろなーと思って解法を書いておきます:

解法:sudo apt install vulkan-validationlayers-devしろ

apt search vulkanと入れると

libvulkan-dev/disco,now 1.1.101.0-2 amd64 [installed]
  Vulkan loader library -- development files

が出てくるのでこれだけいれときゃOK、Vulkanが全部使えるようになるんだぜ!と思いきや、実はこれだけではUbuntu 19.04ではLayerは入りません。

正解はこちらも入れることです:

vulkan-validationlayers-dev/disco,now 1.1.101.0-1 amd64
  Vulkan validation layers -- development files

要するに黙ってsudo apt install vulkan-validationlayers-devしろ。

結果

ちゃんと6つ返ってきました:

[2019/10/30 20:43:35 DEBUG] Layer: VK_LAYER_LUNARG_parameter_validation (spec=4198501, impl=1) :: LunarG Validation Layer
[2019/10/30 20:43:35 DEBUG] Layer: VK_LAYER_LUNARG_object_tracker (spec=4198501, impl=1) :: LunarG Validation Layer
[2019/10/30 20:43:35 DEBUG] Layer: VK_LAYER_GOOGLE_unique_objects (spec=4198501, impl=1) :: Google Validation Layer
[2019/10/30 20:43:35 DEBUG] Layer: VK_LAYER_LUNARG_standard_validation (spec=4198501, impl=1) :: LunarG Standard Validation
[2019/10/30 20:43:35 DEBUG] Layer: VK_LAYER_LUNARG_core_validation (spec=4198501, impl=1) :: LunarG Validation Layer
[2019/10/30 20:43:35 DEBUG] Layer: VK_LAYER_GOOGLE_threading (spec=4198501, impl=1) :: Google Validation Layer

あれ、ちょっとまって、名前全然ちがくない?GOOGLEってなんや??

今日のところはVK_LAYER_LUNARG_standard_validation使って勘弁しといたるわ。

Vulkan難しい

Vulkanとの対話は続く…。

  1. この時点で若干OpenGL時代の命名規則センスを感じヤバみは感じる… []

(機械仕掛けの不完全な)誕生日占い

Posted on

その1

占い師「わたしは本物の霊能力者です。あなたの誕生日をあててさしあげましょう」

「ほんとに?」

占い師「ほんとです。あなたの誕生日の、月の十の位と一の位の差を教えてください」

「6です」

占い師「日の十の位と一の位の差は?」

「5です」

占い師「よろしい。それでは、月の十の位と日の十の位の差は?」

「5です」

占い師「最後に、月と日、十の位と一の位、すべての合計は?」

「13。」

占い師「6月16日ですね?」

「次の行列が正則ってだけじゃん」

その2

占い師「略」

「ほんとに?」

占い師「ほんとです。あなたの誕生日の、月の十の位と一の位の差を教えてください」

「6です」

占い師「次に、あなたの誕生日の、日の十の位と一の位の差に…さらに月の一の位を足したものを教えてくださいな」

「1です」

占い師「最後に、月と日、十の位と一の位、すべての合計は?」

「13。」

占い師「6月16日ですね?」

「次の行列が正則ってだけじゃ…」

「…あれ…正則どころか、そもそも3行しかない…。ほんものの霊能力者は、いるんだ…」

占い師「だから、そう言いましたでしょう?」

その3

占い師「略」

「略」

占い師「あなたの誕生日の、月の十の位と一の位の差を教えてください」

「2です」

占い師「次に、あなたの誕生日の、日の十の位と一の位の差に…さらに月の一の位を足したものを教えてくださいな」

「-5です」

占い師「最後に、月と日、十の位と一の位、すべての合計は?」

「13。」

占い師「Traceback (most recent call last):
File “/src/github.com/ledyba/tanjoubi-uranai/scratch.py”, line 92, in <module>
main()
File /src/github.com/ledyba/tanjoubi-uranai/scratch.py”, line 88, in main
  print(ndict[str(v2.tolist())].transpose())
KeyError: ‘[[13], [-2], [13], [-5]]’

「やっぱな。ちなみにわたしの誕生日は2月29日です。」

ソースコード

https://github.com/ledyba/tanjoubi-uranai


ストップウォッチで計測した月の大きさから、月の直径を計算する

Posted on

今日の朝、ストップウォッチと電線を使って月の天球上での直径を計測したところ、約0.6度と出ました。

…さて、この値はどこまで正しいのか?

それを確かめる一つの方法は、「正解」を直接ググることです。が、それはあんまりにも面白くない。

そこからは少し離れた情報との整合性を、確かめてみようではありませんか。

それは、月の距離と直径です。それぞれ約38万km、3500km。小学校教育と中学受験のせいかおかげか、脳の中に、刷り込まれ、今でも、亡霊のようにこびりつき、こだまする、この「「「事実」」」と、どれほど一致するのかの概算をします。

三角関数で計算する

月までの距離と、月の天球上での直径(角度)が分かれば、三角関数を使って月の直径を計算することができます:

>>> 38*10000 * math.sin(0.6*math.pi/180.0)
3979.277964173401

実際に数値をいれて計算すると、約4000kmと出ました。文部科学省の提唱する「約3500km」と比べるとちょっと大きいですが、倍か半分ぐらいまでの差は出るだろうな思っていたので、それに比べたらだいぶ近い値が出ています。正直、ちょっと驚いています。

テイラー展開でも計算する

さて、仮に文明がいますぐ何らかの理由で崩壊し、コンピュータの提供する便利なsin関数が使えなくなっても、0度近辺でならsin(x)はxで近似できる(テイラー展開)ことを知っていれば筆算で計算することができます:

>>> 38*10000 * (0.6*3.14159265358979/180.0)
3979.3506945470676 #ほんとに筆算しようと思ったが、めんどくさくなってやめた

上の数値と見比べると、38万kmも先のことを計算したのに、100mも差は出ません。実を言うと大学受験の数学や物理の問題か、大学の数学や物理の試験問題でしかテイラー展開なんか使った事がなかったもので、ここまで差が出ないのかと、またもや驚いています。

しかし、でも、ちょっと大きい。

とはいえ、この結果は、教科書に書いてあった「約3500km」に比べるとちょっと大きいのも事実。この計算につかった「約0.6度」は、地平線近くで測ったものです。地平線の近くにある月は、なんとなく、空の上にある月より大きい気がしませんか。それは人間の錯覚なのか、それとも何らかの理由(大気の屈折とか?)で本当に見かけ上大きくなっているのか。

空の上の方の月でも測定して、もう一度同じように計算してみたいです。

しかし、天気予報によれば、これから先しばらく曇りとのこと。

うーん…次の半月か満月の晴れた夜までに、使えそうな電線を見つけておくことにするか。


ストップウォッチを使って月の大きさを測る

Posted on

月を眺めていると、その動きはかなり速いことに気が付きます。

昔のアニメのお歌で「ぼくもわざと立ち止まれば 月も立ち止まる」なんてことばがありましたが、実際に立ち止まってみると、そんなことはありません。動いてるのが目視で分かるくらいの速度で、ゆっくりですが、「歩いて」います(もちろん、だからこのお歌はだめ、なんてことはないですよ)。

さて、このことを使って月の大きさを測ってみました。

方法

月の進行方向と、できるだけ直交する電線を選びます。適当な電線に月が触れるのを眺めたら、ストップウォッチで測定を開始します。そして、月が電線から離れた瞬間に測定終了。その時間を記録して「月の大きさ」とします。「時間」ですが、「大きさ」です。

電線と月の移動する向き(進行方向と呼ぶことにします)は一般にはさまざまな角度をなします。電線と月の進行方向が直交することも、ほぼ同じ方向になることも、ナナメになることも、あるでしょう。「月が電線を横切る時間」を、「月のどこかが電線に触れてから月が完全に電柱から離れるまで」と定義すると、この電線と月の進行方向がなす角度によって、時間が変わってしまいます(すんごい極端な例を考えるとはっきりします)。

月と月の進行方向の直線がまじわる2点を基準にして、この2点が電線と触れる時間と定義して測れば、電線と進行方向の角度の影響を無視できそうな気はするのですが…

まぁ、練習が必要そうなので、今日は「電線と進行方向ができるだけ直交する」一番単純な場合を使って測定します。

結果

4月21日 朝

今日は満月から1日ほど経ったころ。測定結果は次の通りです:

  • 2分16秒
  • 2分23秒

今日は水平線近くが白くて区別がつかなくなってきたので、3度目は断念しました。

約2分20秒とすると、月は29.5日で公転し、地球は1日で自転するので(意図的に細かいところをあえてガン無視している)、このことを使って計算します。

% python
Python 2.7.16 (default, Mar  6 2019, 18:14:51) 
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> moon = 360/29.5
>>> earth = 360.0/1.0
>>> (moon+earth) * 140 / (24*3600.0)
0.6031073446327683
>>> 

天球上の角度はだいたい0.6度?ってことでいいのかな?

今後

理科年表にひょっとすると月の大きさが書いてある気がしてならないのですが、見ないで独自研究をすすめてこうかな。ちょっと悩んでます。

月の満ち欠けみたいに 幸せも 大きくなったり 小さくなったりする」なんて歌詞もあるとおり、月には満ち欠けもあるわけですが、今回の方法を使って月齢を(定量的に)測れたらちょっと面白い気がしています。この場合、月の進行方向と電線の角度にはもっと気をつけないといけないでしょうなぁ。

あとは…そうですねぇ、地平線の近くにあると月は大きく見えるわけですが、これが本当なのかどうか、実測してみたいかも。

太陽

太陽も測ってみたいが、どうすればよいのか…陰を使えばいいのだろうか。

ストップウォッチやめたい

ここでテクノロジーに頼ってるのすこし違和感があるので、ストップウォッチもやめたいんですが、時間を測定するいい道具ないかな。月が電線を横切る時間と、一日の長さ(「日の出から次の日の日の出までの間」としましょう)を同時に測定できる道具…なんだろ。


南極にもIPアドレスはある(???)

Posted on

諸事情で国別IPv4アドレスの一覧を眺めていたのですが、なんと南極にもIPアドレスがあるらしいことに気が付きました。その数、256個。北朝鮮でも1024個あるので、それより輪をかけて少ないのですが、そもそも存在するとは思っていませんでした。だって、南極には国という概念はなじまないし…。

調べて見ると、ARIN(主にアメリカのIPを管理しているところ)が南極に割り当てたようです

arin|AQ|ipv4|23.154.160.0|256|20181017|allocated|b2a4180607264562d56fa4cb02fbc9a4

6つ目の要素は、このIPアドレス範囲が2018年10月17日に予約された事を示しているらしい。すんごく最近ですね。

使われているのか調べる

たった256個となると総当りしたくなるよね…ということで、pingで総当りしてみました:

for i in $(seq 0 255); do
  echo -n "23.154.160.${i}: "
  if ping -c 5 23.154.160.${i} > /dev/null 2>&1; then
    echo 'ok'
  else
    echo 'ng'
  fi
done

結果としては、全てのIPに対して、pingは一切通りませんでした。使われてなさそうです。もちろん、pingが通らないからといって使われていないとは限らないのですが。

南極のインターネット事情を想像する

そういえば、南極の観測基地の人たちはどうやってインターネットやってるんでしょう。データセンターおじさんとしては気になる所です。海底ケーブルが敷設されている…わけないですよね。観測基地はたくさんあるけれど、もちろん、都市に比べたらほとんど無人です。とてもじゃないけど、海底ケーブルなんて採算つきませんもん。でも観測データのやりとりや、連絡を考えるとインターネットは繋がってそうな気はします。衛星通信だろうか。

それとも、実は今まで本当にインターネットが使えなかったのが、最近使えるようになったから、割り当てられたのかな。実際に使えるIPアドレスは254個しかありませんから、昭和基地に割り当てられているIPはせいぜい1個だろうか。うーん、NATを使ってなんとかしのぐつもりか?まぁ、対外向けサーバを運用するわけじゃないから、それでもなんとかなるか。

…などと想像するのも楽しいのですが、ためしに検索してみたところ、昭和基地に関してはKDDIの人が毎年1人派遣されてネットワークの維持管理をしているそうです。衛星経由で南極とKDDI山口衛星通信所を3Mbps(3G回線より遅い)で繋いでいて、(観測優先だけど)隊員はLINEもFacebookも使えるそうな。速度はともかく、意外と現代的だ…。構成的には、LINEやFacebookからは日本のIPアドレスとして認識されるのかな。

まぁ、単に登録を間違えただけかもしれない

whoisをIPアドレスに対して行うと、どの団体に割り当てられたのかをチェックすることができます。南極のIPのうちの1つに対してwhoisした結果はこちら:

% whois 23.154.160.100

#
# ARIN WHOIS data and services are subject to the Terms of Use
# available at: https://www.arin.net/resources/registry/whois/tou/
#
# If you see inaccuracies in the results, please report at
# https://www.arin.net/resources/registry/whois/inaccuracy_reporting/
#
# Copyright 1997-2019, American Registry for Internet Numbers, Ltd.
#


NetRange:       23.154.160.0 - 23.154.160.255
CIDR:           23.154.160.0/24
NetName:        ANYCAST-TEST1
NetHandle:      NET-23-154-160-0-1
Parent:         NET23 (NET-23-0-0-0-0)
NetType:        Direct Allocation
OriginAS:       
Organization:   Windscribe (WL-242)
RegDate:        2018-10-17
Updated:        2018-10-17
Ref:            https://rdap.arin.net/registry/ip/23.154.160.0


OrgName:        Windscribe
OrgId:          WL-242
Address:        9251 Yonge St #8901
City:           Richmond Hill
StateProv:      ON
PostalCode:     L4C 9T3
Country:        AQ
RegDate:        2016-03-07
Updated:        2018-12-21
Ref:            https://rdap.arin.net/registry/entity/WL-242


OrgTechHandle: SAKYE-ARIN
OrgTechName:   Sak, Yegor 
OrgTechPhone:  +1-647-725-2536 
OrgTechEmail:  yegor@windscribe.com
OrgTechRef:    https://rdap.arin.net/registry/entity/SAKYE-ARIN

OrgAbuseHandle: ABUSE5829-ARIN
OrgAbuseName:   Abuse Manager
OrgAbusePhone:  +1-647-727-8859 
OrgAbuseEmail:  abuse@windscribe.com
OrgAbuseRef:    https://rdap.arin.net/registry/entity/ABUSE5829-ARIN

「南極」を示す「Country: AQ」以外の部分の住所は、カナダにある「Windscribe Limited」というVPNを提供する会社の住所と一致しています。…だから、まぁ、申請するときに国コードを間違えて、そのまま誰も気づかないままIPアドレスが割り当てられただけかもしれませんね。

…いや?わかりませんよ。ひょっとすると、国家権力に影響されない最強のVPN…データーヘイブン…を実現するために、南極に拠点を作ろうとしているのかもしれません。イギリスのすぐそばにあった、シーランド公国よりは厄介で、そして頼りになるサービスになるかもしれませんね。知らんけど〜


WordPressの検索機能を悪用するSPAMが押し寄せてきてつらい

Posted on

最近、分散SNS「Pleroma」のインスタンスを立ち上げたのですが(マストドンからもリモートフォローできます: @psi@sabbat.hexe.net)、デバッグのために、nginxのログをtail -fで眺めていて気づきました。ここ二週間ほど、このブログの検索結果のページへ対するリクエストが異常なまでにやって来ていることに。しかもUserAgentは検索エンジンです。クロールしている検索エンジンの会社は様々で、よく知らないドイツの会社?の検索エンジンなどからも来ていました。IPアドレスを逆引きした結果を見る限り、どれも本物の検索エンジンと思われます。

そんなに何を熱心に検索しているのやら、と思って、URLをクリックせずにコピーしてdecodeしてみると、謎の韓国語。Google翻訳に入れると、よくわからないけどなにやらアダルトな雰囲気。

もしやと思って、検索結果が出なかったときに表示される”Nothing found”でこのサイト内をしてみると…:

…これは検索エンジンSPAMですねぇ。間違いない。なんだこれは…。たまげたなぁ。

必ずドメイン名が含まれている事、あと翻訳した文章の内容を見る限り、この韓国語のメッセージを見て、何か期待を膨らませた人がドメイン名を手打ちしてアクセスしてくれることを期待しているのでしょうか。いろんなことを考えるなぁ。

書かなくても分かると思いますが、良い子のみんなはこの画像中のドメインにアクセスしてはなりません

たぶん、これらの長い検索クエリが含まれるURLがずらっと並んだページを、業者?の人がどこかに一生懸命つくって、検索エンジンのbotにクロールを指示しているんだと思います。

今年に入ってのリクエストがほぼSPAMでつらい

% cat access.log | grep "GET https://7io.org\(/?s=\|/search/\)" | wc -l 
1989392
% cat access.log | grep "GET https://7io.org/" | wc -l 
2355234

今年に入ってから処理している235万件ほどリクエストのうち、この検索結果へのリクエスト(ほとんどがこのSPAMだと思われる)は198万件。せっかくCPUをぶん回してリクエストを処理しても84%がSPAMとな。ビットコインのマイニングより虚しいCPUの使い方なんじゃないか。

検索エンジンにインデックスしないようにお願いした

とりあえず、使っているテーマのhead.phpに、検索結果に関してはインデックスしないようにお願いするmetaタグを書きました

でも、これは対症療法にすぎません。検索結果は汚染されなくなりますが、クロールのリクエストは際限なく飛び続けるでしょう(検索エンジンの裏にいる「AI」ってやつがよしなに判断して、アクセスする前に止めてくれるようになる可能性は、無いとは言えませんが)。SPAMをやってる人たちがいつかインデックスされない事に気づいてくれたら止むかもしれませんが、それを期待するのは違和感があります。かといって、検索エンジンを全部ブロックするのもおかしいし。

どうしたもんか。

もはやDOS攻撃に近い

いまのサーバはそれなりの性能があるからあんまり困っていませんけど1、もしラズベリーパイとか、昔使ってた玄箱のような非力な自宅サーバだったら間違いなくCPUのリソースを使い切っていたに違いない。こんなん実質DOS攻撃やんけ

なんとなく、UDP Amplification攻撃にも似ています。UDP Amplification攻撃では、攻撃者はIPアドレスを隠しつつ様々なサーバに元の数倍のトラフィックを流すことが可能なわけですが、このSPAMもどこの誰なのかを隠しつつ様々な検索エンジンを動員して膨大なHTTPリクエストを発生させています。UDP Amplification攻撃と同じように、パケット自体は第三者からやってくるのでブロックするわけにもいかないし、するにしてもキリがない、という点でも似ています。もちろん、本物のDOS攻撃と違ってサーバがダウンしたらSPAMをやってる人たちは目的が達成できなくなるわけですが、まぁダウンしたらその人たちは別のブログで同じような事やるだけですよね、きっと。

インターネット…どうしてこんな事に…

かなしい

2019/03/16 追記

robots.txtに検索ページのURLのパターンを記載することで、検索エンジンからの無意味なアクセスはなくなりました。ページ毎の指定だと一回はやってくるわけですが、この方法ならロボットはアクセスする前に判断してくれるようです。

User-agent: *
Disallow: /feed/
Disallow: /comments/feed/
Disallow: /search/*
Disallow: /*.php$
Disallow: /*?*
Disallow: /*?

 

  1. うそ。nginxのログが肥大化するのでちょっとだけ困ってます。消すからいいけど。 []

HTTPSをやめたらChromeから接続できなくなった話

Posted on

ブンブンハロー、はいどーも。

HTTPSからHTTPへ

最近、わたしと友達がやっている、妖精⊸ロケット(hexe.net)というウェブサイトをHTTPSからHTTPへ移行しました。HTTPSへの移行ではなく、HTTPへの移行です。暗号化するのを、やめました。

んん??時代に逆行しているような気がしますな。Let’s encryptで無料のSSL証明書が簡単に手に入る時代だというのに、お前は一体なにをやっているんだ!

実をいうとHTTPSにも対応してます。HTTPにリダイレクトするけどな!

「保護されていない通信」ですが、何か?

常時https化が叫ばれて久しいですね!

…たったそれだけの理由で、このサイトも含めてなんとなく流されてHTTPSに対応していましたが、…これ、本当に必要なんでしょうか?

そもそも、一体HTTPSで何を保護しているというのか。わたしたちのウェブサイトには公開鍵で署名をしないといけないような、誰が書いたか(あるいは、書いていないか)が極めて重要な、そんなソーシャルな内容はありません。ついでに、このサイトは等しく公衆に公開されているものですから、盗聴されても困らないはずです。まぁ、パケット書き換えられて勝手に改竄されるのは嫌ですけど。でも、ソーシャルな内容でもないのに、わざわざ改竄する人なんか、いるのだろうか?

っていうか、実は常時HTTPSなんかしてもしょうがないから「検索エンジンでの検索結果の順位を上げます(SEOのためにHTTPS対応してね!)」とかいってHTTPSにするモチベーション作ってるんじゃないの。

疑い始めると止まらない!

うぉー、人間が憎い!

「信用できるHTTPSを売りつけてくる人間」が信用できない!

…というわけでHTTPSからHTTPにしてみました。

なぜかChromeでアクセスできない(Firefoxはできる)

設定自体は簡単です。表のnginxが、裏はgoで書かれたバックエンドに対してリバースプロキシしているという構成なので、表のnginxの設定をHTTPとHTTPSでごそっと入れ替えるだけです。

これで際限なく暗号化する潔癖症から逃れられたと思ったのもつかの間。いつも使っているFirefoxでは何の問題もなくHTTPでも表示できたのですが、Chromeでhexe.netを開いてみるとなぜか全く表示されないことに気づきました:

HTTPの方を開いても、勝手にHTTPSへなぜかリダイレクトされてしまいます。サーバはHTTPへリダイレクトし返すので、そこで堂々巡りが発生して「リダイレクトが繰り返し行われました」となってしまったようです。

元々は301でHTTPからHTTPSへ投げていたわけだから、それがChromeのキャッシュに残っていたのだろうか?と思って消してみても、効果はありませんでした。

開発者ツールの「ネットワーク」で確認してみると、301ではなくて「307 Internal Redirect」だそうな:

な、なんじゃそりゃ。307はTemporary Redirectだったはずですが…。そしてもちろん、サーバーでは307を返す設定なんかしていません。

HSTSってのが悪いのか?

そこで思い至ったのがヘッダに書かれているHSTS、一度HTTPで繋いだ時に、次以降は常にHTTPSを利用するよう要請できるという機能でした。HSTSを設定した覚えは一切ないんですが、ひょっとすると、ほら、まぁ、ね、nginxの設定をコピペし間違えるなどして昔設定したことが無いとは言えない…じゃん?

chrome://net-internals/#hstsからHSTSの設定キャッシュは削除できるはずなのですが、やはり効果はありませんでした。変だなぁと思いながら状態を確認してみると:

dynamicなんとかとstaticなんとかの値があって、staticの方にだけ意味のありそうな値が入ってます。ん…static…?

と思って調べていくと、HSTS Preload Listというものに突き当たりました。

ChromeとFirefoxで中身が違うHSTS Preload List

HSTSを指定する方法には、HTTPリクエストへ対するレスポンスのヘッダで指定する方法の他、HSTS Preloadingという方法もあるそうです。

HTTPで最初に接続した時にHSTSを要請する方法の場合、その定義から初回のリクエストは安全でないHTTPで通信しなければなりません。この初回時に改竄でもされたらオシマイです。そこでHSTS Preload Listの出番なのであります。HSTS Preload List Submissionというサイトに「わたしのサイトはHSTSに対応していますよ」と登録しておくと、なんとブラウザの中にその情報が書き込まれて、最初から安全なTLSで通信できるようになりますよ…と。段々宣伝みたいになってきたのでこの辺でやめておきますが、HSTSのRFCの著者の1人がGoogleの中の人なこと、HSTS Preload List Submissionもいかにも「公共」な雰囲気を纏ってはいますがChromium(実質Google)がやってることは指摘しておきましょう。

ここで登録されたリストは最終的に週一の頻度Chromiumのコードに反映されるようです。見てみると…:

ありました。ここでHSTSが有効とマークされているせいで、ブラウザ側でhttpからhttpsへの自動リダイレクトが行われているのでしょう。原因がわかってすっきり。…身に覚えがないけど…。

git blameしまくった結果、この行は2016年11月18日のコミットで追加されたことがわかりました。その時はまだhexe.netは持って無かった気がするので、前の持ち主の人が設定したのかもしれません。前にも使われていた事があるような短いドメイン名はこういう事もあるのでしょう…。

さて、上記のHSTS Preload List SubmissionのサイトではFirefoxでも使われていると書いてありますが、Firefoxのソースコードを参照する限り、hexe.netは入っていません。これで、FirefoxからはHTTPでも見れることが説明できました。…でも、なんで入ってないんでしょう?管理系統が別なんでしょうか。Mozillaのページには特に説明はなく、コミットが3日おきくらいに粛々となされているのは分かるのですが、この変更のソースがどこから来ているのかはコードレビューを見てもよくわかりませんでいた。コードは読めても社会がわからーーーーん!

HSTS Preload ListはWeb標準じゃないけど、Chromeのシェアは大きいから…

このHSTS Preload Listなんですが、Mozillaのページに書いてあるとおり、実はRFCとかW3C勧告のようなWeb標準ではなく、各々のブラウザが勝手に実装したりしていなかったりする「独自拡張」にすぎません。

ところで、こんなニュースがあります:

グーグル、完全HTTPS接続で安全なアプリ用ドメイン「.app」–早期登録を受付開始 – CNET Japan

.appドメインは、アプリ開発者のウェブ運営向けとして用意するTLD。例えば、「アプリ名.app」「開発者名.app」「開発会社名.app」といったURLでアプリの最新情報やダウンロード用リンク、アプリ内コンテンツなどを提供すれば、ユーザーに覚えてもらいやすい、といったメリットがあるという。

最大の特徴は、.appドメインのウェブサイトが必ずHTTPS接続となること。個別にHSTSなどの設定をする必要がなく、.appドメイン内のサイトは自動的にすべてHTTPS接続される。これにより、アクセスするユーザーの安全を手間なく確保できる。

このニュースに対応するように、Chromeの方でもFirefoxの方でも”app”というエントリが含まれています:

Chrome

# https://cs.chromium.org/codesearch/f/chromium/src/net/http/transport_security_state_static.json?cl=0193585e14d2baf5c9ae96a767c532a13973b011

    // gTLDs and eTLDs are welcome to preload if they are interested.
    { "name": "android", "policy": "public-suffix", "mode": "force-https", "include_subdomains": true },
    { "name": "app", "policy": "public-suffix", "mode": "force-https", "include_subdomains": true },
    { "name": "bank", "policy": "public-suffix", "mode": "force-https", "include_subdomains": true },
    { "name": "chrome", "policy": "public-suffix", "mode": "force-https", "include_subdomains": true },
    { "name": "dev", "policy": "public-suffix", "mode": "force-https", "include_subdomains": true },
    { "name": "foo", "policy": "public-suffix", "mode": "force-https", "include_subdomains": true },
    { "name": "gle", "policy": "public-suffix", "mode": "force-https", "include_subdomains": true },
    { "name": "google", "policy": "public-suffix", "mode": "force-https", "include_subdomains": true, "pins": "google" },
    { "name": "insurance", "policy": "public-suffix", "mode": "force-https", "include_subdomains": true },
    { "name": "new", "policy": "public-suffix", "mode": "force-https", "include_subdomains": true },
    { "name": "page", "policy": "public-suffix", "mode": "force-https", "include_subdomains": true },
    { "name": "play", "policy": "public-suffix", "mode": "force-https", "include_subdomains": true },
    { "name": "youtube", "policy": "public-suffix", "mode": "force-https", "include_subdomains": true },

Firefox

# https://hg.mozilla.org/mozilla-central/file/4d15e90af575/security/manager/ssl/nsSTSPreloadList.inc#l3893

...
apothes.is, 1
app, 1
app-at.work, 1
...

ソースコードは非公開だけどInternet ExplorerとかEdgeとかSafariとかでも似たような設定がされてるでしょう。きっと、たぶん。そうにちがいない。

curlではHTTPでアクセスできるけどきっとこれは何かの間違いでしょう:

% curl -vvv get.app
*   Trying 216.239.32.29...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0xe52310)
* Connected to get.app (216.239.32.29) port 80 (#0)
> GET / HTTP/1.1
> Host: get.app
> User-Agent: curl/7.64.0
> Accept: */*
> 
< HTTP/1.1 302 Found
< Location: https://get.app/
< Cache-Control: private
< Content-Type: text/html; charset=UTF-8
< X-Content-Type-Options: nosniff
< Date: Wed, 13 Mar 2019 03:26:39 GMT
< Server: sffe
< Content-Length: 213
< X-XSS-Protection: 1; mode=block
< 
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>302 Moved</TITLE></HEAD><BODY>
<H1>302 Moved</H1>
The document has moved
<A HREF="https://get.app/">here</A>.
</BODY></HTML>
* Connection #0 to host get.app left intact

…うーん、なんか胡散臭いと感じてしまう。

今後どうしようか

  • HSTS Preload List SubmissionRemoval formから削除してもらう
    • 数カ月後のChromeのリリースで、たぶん接続できるようになる
  • HTTPSに戻す
  • すべてを放置し、Chromeからは接続できないウェブサイトを運営していく(自称ダークウェブ)

一番最後でも、良いかなぁ。それぐらいで、ちょうどいいのかもしれない。「地獄少女」みたいで面白くない?(どうかな…)

GoogleのクローラーはHSTS Preload Listを使っていない

ちなみに、GoogleのクローラーはたぶんHeadless Chromeを動かしているけれど、HSTS Preload Listは使っていないと思います。

なんでそんなことが分かるのかというと、次の検索結果からです:

この「WebGL not supported.」はJSを実行した結果DOMに追加される文字列なので、おそらくクローラーはHeadless Chromeを動かしていると思われます。が、このメッセージが出せるということはリダイレクト地獄に陥いっていないということなので、HSTS Preload Listは使ってないと思われます。…ってことは検索結果は改竄されてるかもしれないって事だな!(?)

you can also see