PC-98版東方ゲームのリバースエンジニアリング担当者が引退
Reverse engineer of PC-98 Touhou games retires (rec98.nmlgc.net)
要約
長年のPC-98版東方プロジェクトにおけるリバースエンジニアリングと移植作業から引退することを発表。資金調達の困難さ、AI支援開発(「バイブコーディング」)の台頭、コミュニティの力学の変化などを理由に挙げ、自身のビジネスモデルの限界と今後の展望について詳細に語っています。
全文翻訳
PC-98版東方ゲームのリバースエンジニアリング担当者が引退
📝 投稿日:
🚚 要約:
P0336 ポータビリティ(秋霜玉エンジン/バックエンドコードを共有ポートライブラリに分離)
P0337 秋霜玉(ゲームロジックライブラリ、パート1/4:コードクリーンアップ + 低依存性Win32メモリ管理)
P0338 秋霜玉(ゲームロジックライブラリ、パート2/4:フロントエンドと初期化のクリーンアップ)
P0339 秋霜玉(ゲームロジックライブラリ、パート3/4:ゲームロジックのクリーンアップ + インスタンスクラス)
P0340 秋霜玉(ゲームロジックライブラリ、パート4/4:物理ゲームロジックの分割 / WindowsおよびLinux DLLビルド)
P0341 リプレイ(共有CLIランナー / 秋霜玉の基本実装)
💰 資金提供者:
[匿名], Root, Ember2528, WishMakers, Arandui
🏷️ タグ:
導入はありません。今頃、皆さんも結末を知っているでしょう。私を、そしてプロジェクト全体を壊滅させた唯一の機能へようこそ…
- マイナスの値を持つアイテム?
- 「ゲームロジック」のスコープ定義
- MSVCの
sprintf()におけるロケール肥大化の研究 - MSVCのC++例外処理の安全なバイパス
- ゲームロジックの分離完了
- 共有ReC98ソースポートエンジンの誕生
- リプレイ検証CLI
- 今後の作業
- 引退発表
技術的な話に戻る前に、皆さんの注意を引くために、まず面白い発見から始めましょう。
マイナスの値を持つアイテム?
これは偶然発見したのですが、📝 2024年のコードクリーンアップの別の結果でした。当時、これらのポイントアイテムポップアップのpbgによる元のフォーマットコードには、無視せずに対処することにした多数のコード分析警告の1つが含まれていました。
- sprintf(buf, "%d", e->point);
+ sprintf(buf, "%u", e->point);
e->pointは符号なしなので、ポイントアイテムにマイナスの値が記録されることは決してないはずですよね? さて、アーキテクチャの変更されたばかりの部分をテストしていたときに、これが起こりました。
[動画: 秋霜玉のオリジナルバージョン1.005で、負のポイントを表すアイテム値ポップアップの誤ったレンダリングを示すビデオ。ステージ1の最初の敵を、プレイフィールドの中央に留まりながら撃ち、隣接する敵に突っ込んで即座にライフを失うことで記録されました。これにより、VIVITはプレイフィールドの下端より下でリスポーンアニメーション中にポイントアイテムを回収します。ダウンロード][動画: 秋霜玉のReC98 P0295ビルドで、これらの値を誤って符号なしとして解釈した負のポイントアイテム値ポップアップの誤ったレンダリングを示すビデオ。ステージ1の最初の敵を、プレイフィールドの中央に留まりながら撃ち、隣接する敵に突っ込んで即座にライフを失うことで記録されました。これにより、VIVITはプレイフィールドの下端より下でリスポーンアニメーション中にポイントアイテムを回収します。ダウンロード]
見事なアンダーフローです。
驚くべきことに、ゲームはポイントアイテムの値を0にクランプしていませんでした! コレクション値の計算式を見ると、そこで何が起こるかがすぐにわかります。
(((469 - Viv.y) + (Viv.evade × 4)) × 160)
これは、VIVITのY座標が正確に469にクランプされる通常のゲームプレイでは問題なく機能します。また、エバイドチェーンが空の状態でボトムハギング中にポイントアイテムを回収すると0点になる理由も説明しています。しかし、VIVITが何かに衝突してライフを失うと、彼女は1ピクセル/フレームの速度で上昇する前に、Y座標が即座に659に設定されます。これにより、ポイント計算式が負の値を生成する127フレームのウィンドウが作成されます。アイテムは中心のY座標が487ピクセルより大きい場合に消滅しますが、このウィンドウを数フレームに狭める試みはほとんど意味がありません。
- アイテムはVIVITとの衝突判定の後にのみ消滅するため、487ピクセルより下でVIVITが回収できる追加のフレームが1フレーム与えられます。
- アイテムは毎秒6ピクセルの最大速度まで、毎フレーム3/64ピクセル、つまり0.046875ピクセルの割合で加速します。VIVITは自身のY座標より23.984375ピクセル高い中心Y座標を持つあらゆるアイテムを回収するため、リスポーン中のVIVITがアイテムを回収できる最も早いY座標は(487 + 6 + 23.984375) = 516.984375ピクセルとなり、ウィンドウはVIVITが516.5から470ピクセルの間にあるわずか32フレームに限定されます。
- しかし、その制限を考慮しても、ステージスクリプトはプレイフィールドの境界外に敵を(そして実際に)スポーンさせることができ、ショットやボムで自由に破壊できます。
明らかに、この問題に対して何らかの対策を講じる必要があります。しかし、何を、どのように?
- まず、これらのポップアップに使用される5x7ピクセルのフォントにはマイナス記号のスプライトが定義されていません。元の
%dフォーマット指定子は文字列にマイナス記号を追加しましたが、ポップアップレンダラーは数値以外の文字を単純にスキップしました。これらの負のポイントアイテム値はゲームに常に存在していましたが、マイナス記号が表示されないため、通常の正の値のポップアップと区別がつきませんでした。コード分析が%dを%uに変更するように私を説得しなかったら、この問題全体に気づかなかったかもしれません。 - 次に、プレイフィールドの左上隅に表示されるスコアは増加するだけで、決して減少しません。したがって、負のスコアポイントは、デルタが再び正になるまで他のスコアソースを吸収する「スコアデルタ」の一部としてのみ「存在」します。
負のデルタを即座に表示される合計スコアに反映させることはできます。
[動画: 秋霜玉のオリジナルバージョン1.005で、負のポイントを表すアイテム値ポップアップの誤ったレンダリングを示すビデオ。ステージ1の最初の敵を、プレイフィールドの中央に留まりながら撃ち、隣接する敵に突っ込んで即座にライフを失うことで記録されました。これにより、VIVITはプレイフィールドの下端より下でリスポーンアニメーション中にポイントアイテムを回収します。ダウンロード][動画: マイナス記号の定義済みスプライトと、負のデルタの公式スコアへの即時加算で、負のアイテム値ポップアップがどのように見えるかを示すビデオ。ステージ1の最初の敵を、プレイフィールドの中央に留まりながら撃ち、隣接する敵に突っ込んで即座にライフを失うことで記録されました。これにより、VIVITはプレイフィールドの下端より下でリスポーンアニメーション中にポイントアイテムを回収します。ダウンロード]
しかし、その数値は単なる中間値ではなく、ハイスコアリスト、そして将来のリプレイフォーマットに記録される実際の内部および公式スコアを表します。ゲームが終了する前にデルタがスコアに完全に加算されないコードパス(例えば、📝 TH04のステージ6とExtraの終了時)がある場合、pbgのオリジナルビルドで同じゲームプレイを行った場合とは異なるスコアになります。そして、案の定、秋霜玉も同じ問題を抱えています。デルタはゲームオーバー時にのみ公式スコアに完全に加算され、それ以外の場合はVIVITの更新関数の一部として徐々に加算されるだけで、1フレームあたり最大10万ポイントの増分です。もし即座に負のデルタをコミットし、プレイヤーが負の値を持つポイントアイテムを回収した直後に終了した場合、その部分的なランの公式スコアは、オリジナルゲームでのスコアよりも低くなります。これは、ラウンドを終了した場合にハイスコアリストにスコアを入力しない秋霜玉では、現時点では問題になりませんが、これらの部分的なランをリプレイとして保存し始めると(当然、そうしたい)、問題になります。しかし、それ以外にも、ゲームの明確に定義されたスコアリング側面へのいかなる変更も正当化するには、最終的にはあまりにも些細な問題です。表示スコアと記録スコアの間の新たな不整合を導入することさえもです。
したがって、残されたのは、マイナス記号の欠落というレンダリングバグを修正することだけです。これは、システムスプライトシートの#DCDCFEの様々な5x1ピクセルラインの1つを再利用することで簡単に修正できます。
では、秋霜玉のゲームロジックをライブラリに分割しましょう! ステップ1はもちろんこれです。
「ゲームロジック」のスコープ定義
一方では、答えは単純に見えます。GameMove()関数は、1フレームのゲームプレイに関わるすべてのサブシステムをきれいにリストアップしています。では、それらすべてを取り込み、DLLに移動させて、完了ですよね? さて、このリストのサブシステムをそのまま取るのではなく、実際の最小限のゲームプレイ関連コードを特定して分離するのには、いくつかの良い理由があります。
-
パフォーマンス。理想的には、この分離されたゲームロジックコアを、すべての新しいリリースと過去に作成されたすべてのリプレイに対して一括検証に使用します。数ミリ秒の未必要サブシステムでも、この規模ではすぐに積み重なります。
-
スレッドセーフティ、並列処理、および関連するリファクタリング作業。pbgはほとんどのゲームプレイ状態をグローバル変数に保存していたため、それらをすべて単一のC++クラスにバンドルして、複数回インスタンス化できるようにしたいと考えています。結局のところ、📝 プロセス起動コストはWindowsで30〜40ミリ秒かかりますので、リプレイバリデーターをマルチスレッド化する方がはるかに望ましいです。影響を受けるサブシステムを最小限に抑えることで、必要なリファクタリング作業の量を最小限に抑えます。
また、リプレイの逆方向シークはどうでしょうか? 👀 そのリファクタリング作業は、 snappy なフレームレートで動作するために必要なゲームデータのいくつかの「キーフレーム」スナップショットの基盤も築くでしょう。今正しく行うのが最善です。 -
すべてのロジックコードを新しい
ssg/ディレクトリに配置し、GIAN07/をプレゼンテーションの側面のみに残した場合、このコードベースで作業する人は誰でも、ssg/のコードを変更するとゲームプレイが分岐して既存のリプレイが壊れるリスクがあることをすぐに理解できます。しかし、この物理的な分離から得られるガイダンスと暗黙のドキュメントは、ssg/のコード量を最小限に抑え、GIAN07/の自由に編集可能なコードとデータの量を最大化する努力をしない限り、真に意味のあるものにはなりません。 -
ゲームロジックDLLはReC98リリースバージョン番号で自身を識別しますが、すべての空白文字を削除したゲームロジックソースコードのハッシュもハードコードすることを計画しました。このハッシュは、リプレイがどのゲームビルドで記録されたかを識別できる第二のレイヤーとして、すべてのリプレイに追加されます。したがって、ハッシュされるコード量を最小限に抑えることで、この識別ハッシュがバージョンやフォーク間で変更される可能性を最小限に抑えます。
-
GameMove()の最上部には、ゲームのダイアログボックスを処理するMWinMove()の呼び出しがあります。私の📝 新しい遅延更新の即時モードテキストレンダリングアプローチにより、各テキストボックスは現在、その正確な内容を保持するために動的に割り当てられたstd::stringを必要とします。しかし、ダイアログは、ゲームプレイ中に動的に割り当てを行う唯一のサブシステムでもあります。このシステム全体が、新しいリプレイフォーマットでのダイアログ表示の計画されたサポートがあってもゲームロジックに影響しないことを証明できる場合、それをフロントエンドに残す良い理由になります。そして、それを証明できるため、これはC/C++標準ライブラリの肥大化(したがってOS API依存性)を削減するという pretty bonus goal の大部分を達成します。 -
そして、すでに1つのサブシステムを含めたくない場合、正しい数のサブシステムを含めないこともできます。
マップタイルはおそらく、フロントエンド専用の装飾データとして最も注目すべき例です。VIVITは自分が何の上を飛んでいるか気にしたことがないので、新しいロジックレイヤーはそれらを処理してスクロールするだけでなく、ステージごとのマップデータをロードして解凍する必要さえありません。必要なのはENEMY.DATからのECLとSCLスクリプトだけです。
これにより、秋霜玉のゲームロジックを構成するサブシステムのリストはかなり簡潔になります。
Yup. 秋霜玉は、見かけ上のゲームプレイ側面から装飾的なパーティクルまで、すべてに単一のRNGを使用しています。リプレイ可能なゲームで使用される疑似乱数生成器の種類について最初に学ぶことは、それらがステートフルであるということです。それらを指定されたシードで初期化すると、同じシーケンスの値が得られ、rand()相当の各呼び出しで内部状態が次の数値に進みます。したがって、明確に定義された順序になります。
…
31305
22709
32031
30508
339
32731
29375
30469
14300
…
リスポーンした岩
敵の初期化
ECLスクリプト
エバイドパーティクル
ステージ4の開始近くのフレームで取得された乱数の代表的なシーケンス。
したがって、RNGのシーケンスから数値を引き出す可能性のあるすべてのサブシステムは、定義上、ゲームロジックと見なされなければなりません。これらのサブシステムが
- 異なる順序で処理される(これはゲームロジックと見かけ上のプレゼンテーションをきれいに分割することの自然な副作用でしょう)、または
- 全く処理されない(ゲームロジックレイヤーのみを実行するCLIツールの場合)
場合、結果の乱数列は既存のリプレイが記録されたシーケンスと一致しなくなり、多かれ少なかれ即座の同期ずれを引き起こします。これは、ちなみに、TH03のネットプレイが当初予想されていたよりも長く遅延した理由と同じです。TH03もゲームプレイとビジュアル要素の両方に単一のRNGを使用しており、ロールバックはネットプレイの譲れない基本機能であるため、レンダリング関数内のRNG呼び出しを分離し、それらをそれぞれの更新関数に移動するために、RE時間を費やす必要がありました。
そして突然、このリストは結局、いくつかの装飾的な側面に数個追加されます。
pbgが翻訳可能なテキストの主要部分のレンダリング呼び出しにランダム性を追加しなかったことは、少なくとも安心できます。例えば、BGMタイトルアニメーションが各グラフェムクラスターのレンダリングプロパティをランダム化すると想像してみてください。それは翻訳計画に大きな障害となるでしょう。
とはいえ、ゲームはまだ私のヘッドレスライブラリ計画に小さな障害をもたらしています。秋霜玉は、最初に言及した個々の文字の文字列にボーナスとエバイドポイントをどのように変換するのでしょうか? まあ、もちろん、Cプログラミング言語が提供する最も自然なパスを取ります。
sprintf(buf, " Bonus %7uPts", temp);
sprintf(buf, "%3d Evade %7dPts", Viv.evade, Viv.evadesc);
これにより、これらの用語の翻訳に対する最大文字数制限も確立されることに注意してください。
そして、結果の文字列内のすべての文字(スペースさえも)が独立してRNGから移動して描画されるため、これは、秋霜玉が標準準拠の長さ修飾子サポートを備えたsprintf()実装に依存していることを意味します。リプレイが同期ずれしないようにするためです。前述のように、ゲームロジックがC標準ライブラリ関数に過度に依存しないようにしたかったのですが、ロケール肥大化を引き起こすWindowsバイナリに、常に私の頭痛の種であった関数に依存することになります。
sprintf()をここから削除することはできませんか? 結局のところ、2つの呼び出しだけです。エバイド文字列については、エバイドカウンターとエバイドスコアは3桁の999と7桁の9,990,000を超えることはないので、簡単に処理できます。したがって、固定サイズの静的文字列を提供し、個々の桁をこの配列内の固定オフセットに分割することができます。しかし、ボーナス文字列のtemp変数は、理論的には最大で…
| Small bullets | ((10,000 + (999 × 100)) × 600) + |
| Large bullets | ((15,000 + (999 × 100)) × 1803) |
…273,104,700ポイント、これは9桁であり、フォーマット文字列で割り当てられた7桁よりも多く、手動アプローチをかなり面倒にします。フロントエンドもすべての動的なメニューテキストにsprintf()を使用しているため、sprintf()を完全に破棄しない限り、メインバイナリにリンクされ続けます。
しかし、このロケール肥大化は本当にそれほど悪いのでしょうか? 純粋さの追求は、それだけの価値がないのかもしれません。MSのsprintf()が依存しているシステムAPIまでの関数呼び出しチェーンをトレースしてみましょう。
sprintf()__stdio_common_vsprintf()__crt_cached_ptd_host::__crt_cached_ptd_host(_locale_t)nullptrで呼び出され、- PTDのロケールポインタを
__acrt_initial_locale_pointersから初期化します。
これらのロケールポインタは、Cランタイムの初期化時に以下の呼び出しチェーンを介して設定されます。
__scrt_initialize_crt()__acrt_initialize()__acrt_execute_initializers()__acrt_initialize_multibyte()、これは🚨FlsGetValue()と🚨FlsSetValue()を呼び出します。setmbcp_internal()、これはgetSystemCP()を介してGetACP()を呼び出します。_setmbcp_nolock()、これはIsValidCodePage()とGetCPInfo()を呼び出します。setSBUpLow()、これは🚨GetStringTypeW()と🚨LCMapStringW()を呼び出します。
これだけでは、それほどひどい、あるいは肥大化しているようには見えません。🚨マークの付いた4つのAPI呼び出しはWindows 9xでは利用できませんが、📝 昨年、その対処法はすでに見つけています。しかし、より大きな問題は、これが低Win32 API依存性を持つ美しいスタンドアロンゲームロジックライブラリを構築するという私の計画にとって何を意味するかということです。Cランタイムの初期化はすべてか無かであり、それを削除すると、sprintf()さえも使用できなくなります。なぜなら、それらのCRTロケールポインタは初期化されないままになるからです。
しかし、今では興味があります。このANSIコードページ取得コード全体は、sprintf()に関して実際には何をもたらすのでしょうか?
- 浮動小数点数をフォーマットする際のロケールに正確な小数点区切り文字。
- ロケールの現在のコードページで2バイトコードポイントの途中でフォーマット文字列が終わる場合にエラーコードを返します。(ただし、これは2018年10月にリリースされたSDKバージョン10.0.17763.0で停止しました。Windows SDKで、この行にひどい符号拡張バグが導入されました。詳細は読者の演習とします。私は確かに良い笑いを得ました。)
- UTF-16文字列を
charバッファにフォーマットするための、非標準の%Sおよび%C指定子。
したがって、ロケール使用がinfamously、長らく爆弾でしたという1つの機能、非ハードコードフォーマット文字列の非常に安全でない使用例(MSVCの/execution-charsetはコンパイル時にハードコードフォーマット文字列の有効性をチェックするため)にのみ関係する1つの壊れた機能、そしてWideCharToMultiByte()呼び出しの単なる非ポータブルな構文糖衣にすぎないように見える3番目の機能です。最初のロケールのコードページで。
その3番目の機能は、Windows 9xをサポートする必要があり、その比較的長いWideCharToMultiByte()呼び出しをタイプしたくない場合に役立ったかもしれません。しかし、その場合でも、構文の簡潔さは、1つだけの変換動作を表すという代償を伴い、それはかなり曖昧な結果をもたらします。
UTF-16ソース文字列
変換コードページ
932 (Shift-JIS)
1251 (Windows Cyrillic)
1252 (Windows Latin 1)
65001 (UTF-8)
日E565 本2C67 語9E8Aк3A04 и3804 р4004äE404 öF600 Ÿ7801🗺3DD8FADD
日93FA 本967B 語8CEAк847B и8479 р8482a61 o6F
(空)
(空)
日E697A5 本E69CAC 語E8AA9EкD0BA иD0B8 рD180äC3A4 öC3B6 ŸC5B8🗺F09F97BA
к3A04 и3804 р4004äE404 öF600 Ÿ7801🗺3DD8FADD日E565 本2C67 語9E8A
к847B и8479 р8482a61 o6F
кEA иE8 рF0a61 o6FY59
(空)
(空)
(空)
🗺3DD8FADD日E565 本2C67 語9E8Aк3A04 и3804 р4004äE404 öF600 Ÿ7801
(空)
(空)
(空)
🗺F09F97BA日E697A5 本E69CAC 語E8AA9EкD0BA иD0B8 рD180äC3A4 öC3B6 ŸC5B8
一方では、このような古いAPIがUTF-16サロゲートペアを正しくデコードしてUTF-8バイトの有効なシーケンスに変換できるとは期待していませんでした。CESU-8の罠に陥ることなく。しかし、最初の非表現可能なコードポイントで常にerrno == EILSEQでエラーコードを返して失敗するのではなく、いずれかの置換文字を選択するのではなく、この変換を行うのに最適な選択肢は本当にあったのでしょうか? そして、このコードページ依存のASCII音訳は何なのでしょうか?äとöはすべてaとoに変換されますが、ŸはCP1251でのみYに変換され、CP932では無効として扱われます。
しかし、さらに悪化します。これらの結果でさえ、Cランタイムのグローバルコードページを指定または暗示するロケール文字列でsetlocale()を使用する、または同じ種類のロケール文字列から作成された専用のロケールオブジェクトでsprintf_l()を使用する必要があります。そうです、上記でトレースしたすべてのCランタイム起動コードは、%Sおよび%Cによって行われるワイド→ナロー変換のために特定のコードページを設定すらしていません。代わりに、これらの変換から有用な結果を得るために、追加の約34.5 KiBのC標準ライブラリの肥大化をリンクする必要があります。そうしないと、これらの変換はデフォルトのCロケールの専用ロジックを使用します。これは役に立つどころか、さらに悪いです。
| UTF-16ソース文字列 | Cロケール |
|---|---|
| 日E565 本2C67 語9E8Aк3A04 и3804 р4004äE404 öF600 Ÿ7801🗺3DD8FADD | (空) |
| к3A04 и3804 р4004äE404 öF600 Ÿ7801🗺3DD8FADD日E565 本2C67 語9E8A | (空) |
| äE404 öF600 Ÿ7801🗺3DD8FADD日E565 本2C67 語9E8Aк3A04 и3804 р4004 | äE4 öF6 |
| 🗺3DD8FADD日E565 本2C67 語9E8Aк3A04 и3804 р4004äE404 öF600 Ÿ7801 | (空) |
Latin-1ですが、Ÿなしですか? いいえ、これはもっとひどいものです:
if(wchar > 255) {
memset(destination, 0, destination_count);
return ptd.get_errno().set(EILSEQ);
}
*destination = static_cast<char>(wchar);
Cロケールでは、これらのフォーマット指定子は単に最初の256のUnicodeコードポイントを取得し、8ビットに切り捨て、それ以外のすべてで失敗し、有効なASCIIでもUTF-8でもないエンコーディングのバイト文字列を生成します。結果のバイトは、この場合CP1252として解釈すると元の文字列と一致しますが、UnicodeのLatin-1 Supplementブロックの最後の75%がCP1252と一致し、最初の2文字がちょうどその範囲に入るためです。これもŸが消える理由です。上記で見たように、ŸはCP1252では0x9Fですが、UnicodeではU+0178なので、変換に失敗します。そして、UTF-16文字列にC1制御文字(U+0080からU+009Fの間)が含まれていた場合、結果の文字列も有効なCP1252にはなりませんでした。
このばかげた非エンコーディングは、最近のフォールバックパスだけでなく、1992年のMicrosoft C/C++ 7.0にまで遡ります。そのCランタイムは、Microsoftの最初の予備的なwctomb()関数の実装を特徴としていました。当時、MicrosoftはまだCRTにロケールを実装していなかったので、この保護されたwchar_tからcharへのキャストは、関数が行った唯一のことでした。
そして悲劇的な出来事として、POSIXの弁護士は、Windows 3.0時代のこの予備的なハックがPOSIX標準のセクションXBD §6.2を実装していると主張できます。
- ISO Cは、
wctomb()が現在のロケールを使用することを示していますが、ロケールのエンコーディングは指定されていません。 - しかし、2016年のXBD/TC2/D4/0029修正以降、POSIXはデフォルトの
Cロケールが単一バイトエンコーディングで正確に256文字をエンコードすることを指定しています。 - このエンコーディングは、ASCII文字セットがどこかに現れるという規定以外は指定されていません。実装はASCIIを任意のコードポイントにマッピングし、その後、他の128文字を自由に選択できます。
- 明らかに、固定の256文字エンコーディングを2016年の標準に修正することは完全に不条理です。標準が常にそう言うことを意図していたからですが、POSIXは常に正しいです。🤓
- そして結局、この動作は、ワイド文字列がこれらの特定の256文字のみを使用する限り、ナローとワイドの表現間のロスレスラウンドトリップ変換を保証するため、有用です!!1! 🤓
これにより、MicrosoftとPOSIX警察の両方が、この古いコードを削除するよりも保持する方が良いと合意している状態になります。後方互換性!
しかし、私は十分に見ました。現代のC++(さらにはC)プログラムは、これらのいずれも必要としません。スタンドアロンゲームロジックDLLの純粋な光学系でさえ、はるかに意味があり、秋霜玉のすべてのコードからMicrosoftのsprintf()実装を削除することを正当化するのに十分です。
置換は3つの形式のいずれかで提供できます。
- SDLは
snprintf()を提供しており、pbgのコード全体で使用されている安全でないsprintf()との違いを抽象化できます。しかし、SDLはビルド時のvsnprintf()の可用性に基づいて、独自のノンスプラッシュ実装を選択します。これは、Linuxのほとんどの事前コンパイル済みSDLパッケージではおそらく与えられており、回避しようとしていたコアゲームロジックのシステム固有の動作に戻ってしまいます。また、ロジックDLLはSDLに明示的に依存しないべきではありません。 - nanofmtのような型安全でモダンなC++代替手段は理論的には良いですが、すべての
sprintf()呼び出しサイトを書き直す必要があるため、非常に破壊的です。また、コード生成は非常に残念であり、miniaudioのロガーにもソリューションが必要になります。これもsprintf()を使用しています。 - 幸いなことに、ドロップインの代替として機能する、最小限でノンスプラッシュの
sprintf()実装はたくさんあります。eyalroz/printfはすべての要件を満たし、活発にメンテナンスされており、最小構成でわずか3.5 KiBのコンパイル済みコードサイズです。miniaudioをその実装にリダイレクトするには、残念なヘッダーインクルードハックが必要ですが… sigh、このライブラリをこのコードベースから完全に削除したいです。
MSVCのsprintf()実装が削除されたので、DllMainCRTStartupの前にCランタイムの初期化を安全にバイパスし、バイナリに引き込まれる肥大化を回避し、システム依存性のない完全にスタンドアロンのssg.v1.005.dllを得ることができます。
#pragma comment(linker, "/ENTRY:DllMainCRTStartup")
int __stdcall DllMainCRTStartup(void *, uint32_t, void *)
{
return 1;
}
はい、それがすべてです。/NODEFAULTLIBは物事をはるかに難しくするでしょう。
これにより、リンカエラーが2つだけ残ります。
error LNK2001: unresolved external symbol ___CxxFrameHandler3
error LNK2001: unresolved external symbol ___std_terminate
再びため息。…の調査に時間を費やす時が来ました。
MSVCでのC++例外処理
…そして、関連するコンパイラ警告とフラグが実際に何を意味するのかを最終的に学びます。それは1つのことから始まります。
warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc
この警告は、pbgが例外を制御フローに使用した数少ない面白いインスタンスから発生します。それらを書き直した後、/EHscを再度削除して問題ないはずですが…
…しかし、📝 2年前にC++標準ライブラリモジュールに移行したためです。これらのモジュールを作成するには、C++標準ライブラリ全体を事前にコンパイルする必要があります。Microsoftの実装は、どこでも例外をスローしてキャッチする可能性があります。モジュールコンパイルコマンドラインから/EHscを削除すると、<chrono>のget_tzdb_list()で発生する、アルファベット順で最初のC4530ケースに遭遇するまで、それほど時間はかかりません。
OK、しかし一部の標準ライブラリ関数が例外をスローしてキャッチする可能性があるという事実は、私たちのコードには影響しないはずです。では、例外のキャッチがその意図された動作に不可欠であり、メモリをすべて使い果たすよりも簡単にトリガーできる機能で実験してみましょう。
import std;
#include <stdio.h>
struct Inner {
~Inner() {
puts("Inner destroyed");
}
};
int main()
{
// `std::packaged_task`は、初期化された呼び出し可能なオブジェクトの戻り値または例外を格納します。
// これは明らかに標準ライブラリ内に例外ハンドラを必要とし、したがって`/EHsc`の良いテストケースとなります。
std::packaged_task<void()> task([] {
Inner instance;
throw "from task";
});
task(); // タスクを実行
// `#define try` は例外処理をスキップできます
try {
puts("Rethrowing exception");
task.get_future().get();
}
#ifndef try
catch (const char *error) {
printf("Caught exception %s\n", error);
}
#endif
puts("Returning");
return 0;
}
そして、stdモジュールレベルとソースレベルの両方で/EHscのすべての順列でコンパイルし、最終ハンドラを削除してコンパイルしてみましょう。
モジュール用の/EHsc | ソースファイル用の/EHsc | try/catchブロック有効 | ソースファイルへの警告 | 例外動作 |
|---|---|---|---|---|
| ❌ | ❌ | はい | C4530 | 💣 キャッチされたが、アンワインドされなかった |
| ❌ | ✔️ | はい | C5050 | ⭐ キャッチされ、アンワインドされた |
| ✔️ | ✔️ | はい | ⭐ キャッチされ、アンワインドされた | |
| ✔️ | ❌ | はい | C5050, C4530 | 💀 コンパイラ内部エラー |
| ❌ | ❌ | いいえ | C4530 | ✔️ 終了した |
| ❌ | ✔️ | いいえ | C5050 | ✔️ 終了した |
| ✔️ | ✔️ | いいえ | ✔️ 終了した | |
| ✔️ | ❌ | いいえ | C5050, C4530 | 💀 コンパイラ内部エラー |
C++モジュールがまだエッジ技術であり、コンパイラをクラッシュさせる可能性があるとは驚くべきことです。導入から2つのC++標準バージョン後です。これらは、この開発サイクルで見た唯一の内部コンパイラエラーではありませんでした。
いずれにせよ、これらの結果は期待していたよりもさらに良いです。
-
/EHsc、したがってC4530は、モジュールコンパイル中にはまったく重要ではないようです。モジュールをインポートするソースファイルに/EHscを設定すると、標準ライブラリコードのスタックアンワインドが再アクティブ化されるようです。 -
なぜそうなるのでしょうか?
std.ifc(ビルド済みモジュールインターフェイスファイル)もstd.obj(リンクコマンドラインに追加するコンパイル済みオブジェクトファイル)も、実際のコンパイル済みコードを含んでいないためです。IFCには解析および分析されたコードのバイナリ表現が含まれていると思われますが、少なくとも一部のコンパイル済みコードが.objに含まれていると考えるかもしれません。しかし、そうではありません。標準ライブラリによって参照される静的データのみが含まれています。実際、stdモジュール内のすべてのコードはヘッダーから来るため、One Definition Ruleを維持するためにテンプレート化されているか、inlineとして宣言されている必要があります。したがって、IFCファイルからのバイナリ表現は、そのユニットの例外処理ルールに基づいて、インポートされる各翻訳ユニットでインスタンス化および特殊化されるだけです。MSVCのフロントエンドはおそらく、/EHscの不在が実際に重要になるずっと前の、非常に早い分析段階でC4530を発行します。
(はい、IFCは多くの事前コンパイル済みコードを参照していますが、それらのコードはすべてMSVCのランタイムライブラリの一部です。) -
唯一のクリティカルなケースは最初のケースで、
/EHscなしでcatchを実行し、したがって実行されると予想されるデストラクタの実行をスキップします。しかし、この場合、MSVCはまさにこの問題から私たちを保護するためにC4530を発行します。この具体性は、C++コンパイラによって発行された最高の診断の1つであるGCCの📝-Wformat-overflowにC4530を押し上げます。 -
そのため、堅牢な戦略を導き出すことができます。モジュールコンパイル中にC4530を無効にし、他のすべてのビルドコマンドラインでエラーとして扱います。これにより、
/EHscなしで安全にすべてをコンパイルできます。なぜなら、/EHscの不在が重要になるコードを記述すると、コンパイルエラーが発生するからです。 -
そして、素敵なボーナスとして、MSVCはDLL境界を越えて例外が飛ぶことを考慮する必要がなくなるため、DLLエクスポートクラスコンストラクタを適切に最適化できます。
かなり進みましたが、結局、このすべての肥大化削減は本当に重要だったのでしょうか? 絶対に、私に言わせればそうです。
[画像: Windows 98で実行中のDependency Walkerのスクリーンショット。新しい秋霜玉ゲームロジックDLLを表示し、GetProcessHeap()、HeapAlloc()、HeapFree()の3つの最小限のインポートと73,216バイトのサイズを示しています。]
そして、これら3つのインポートすべてが、私たち自身のメモリ管理コードから来ています。Cランタイムの初期化を削除しなかった場合、DLLは157,184バイトに肥大化していました。これは最終的なサイズの2倍以上です。
CおよびC++標準ライブラリの2つの残念な側面について学び、美しく、小さく、スタンドアロンのバイナリを受け取りました。これはネイティブで95以降のすべてのWindowsバージョンで実行され、それを行うためにそれほどひどく苦労する必要もありませんでした。現在のVisual Studioでコンパイルし続けているにもかかわらずです。これに取り組んだとき、/NODEFAULTLIBを使用し、少なくともSDLのCランタイムポリフィルにリンクする必要があると思っていましたが、それさえも必要ありませんでした。代わりに、DLLはまだセキュリティクッキー、32ビットx86でのMSVCの組み込み64ビット数学関数、およびstd::unique_ptr(採用された生のポインタとカスタムデリーターを含む)、rangesとviews、spans、std::invokeなど、幅広い最新C++標準ライブラリ機能を使用できます。
さて、ビルド品質には満足しているので、実際のミッションのステップ2に進みましょう。
ゲームロジックの分離完了
これは間違いなく、この開発サイクルの最も過酷な部分でした。ラウンド初期化でさえ、コードをクリーンアップし、通常の6ステージラウンド、Extraステージラウンド、リプレイ、および事前記録されたデモで機能する一貫したAPIを最終的に得るために、フルプッシュの大部分を必要としました。このコードベースは本当に呪われています。新しいコードを見ると、手続き的な状態変更の混乱が見られ、それを触る前に合理化しなければなりません。😩
では、すべてのゲーム状態をバンドルし、プレイヤー入力に基づいてゲームプレイの1フレームを実行する中央メソッドを提供する単一のC_SSGクラスにどのように到達するのでしょうか? コードの変更を最小限に抑え、基本的にゲーム全体をゼロから再構築しないようにしたい場合、コードの元のグローバル変数と関数の束から現実的なパスは2つあります。
-
すべてのゲームプレイ状態と関数を単一のクラスに投げ込みます。これにより、ヘッダーファイルがいくつか削除されますが、.CPPファイルは大部分が同じままで、主にフリー関数からクラスメソッド構文への関数定義ヘッダーの変更が必要になります。
-void fragment_move(void) +void C_SSG::fragment_move(void)その一方で、ゴッドオブジェクトができ、そのクラス宣言へのすべての変更がロジックDLLのすべての.CPPファイルを再コンパイルすることになります。
-
コードの元のサブシステム分離を維持し、各サブシステムを独自のクラスに変え、各サブシステムが必要とする他のサブシステムへの参照のみを与えることで、激しい状態変更の可能性を制限します。これらの参照の一部は
constにすることもでき、追加の安全性と意図の文書化を提供します。その後、C_SSGはこれらのすべての小さなクラスをバンドルし、巨大なスイッチボードとして機能し、すべてのサブクラスを相互に接続します。これは、数百行の参照管理ボイラープレートのコストで、ゲームの既存のコード構造を維持します。ただし、ヘッダー依存性を最小限に抑えるために、前方宣言をうまく活用できます。
当初、私はオプション1に傾いていましたが、タイプし始めて数分でそれを嫌悪しました。したがって、サブシステムごとに1つのクラスにします。これは、サブシステム間の各関数呼び出しにおいて、.CPPファイルへの影響がわずかに大きくなりますが、それらはレビューが容易です。ほとんどの場合、キャメルケースの単語間の追加のドットアクセサです。
しかし、これもまた、元のファイルレイアウトを精神的にのみ維持しています。完了するとすぐに、そのソリューションも後悔しました。ロジック翻訳ユニットの不要な再コンパイルを回避できたかもしれませんが、この分割の性質全体が、すべてのゲーム状態レンダリングコードを別の場所に移動する必要がありました。そして、レンダリングされたサブシステムごとに1つの小さなソースファイルを持つことはあまり意味がなかったので、単一の巨大なファイルを選択しました。
そして、念のため、最終的にC_SSGのサイズがどれくらいになったかというと、32ビットシステムで403,616バイト、64ビットシステムで405,984バイトです。
このロジック/プレゼンテーションの分割と結果のAPIの一部として、さらに興味深い実装上の決定をいくつか行う必要がありました。
-
上記の
KERNEL32.DLLインポートリストからすでに気づいているかもしれませんが、はい、ゲームロジックDLLにファイルI/Oを追加しないことにしました。DLLが使用される言語にそれを任せる方がはるかに理にかなっています。そのような言語の高レベルファイルI/O関数は、FFI境界を越えて私がまとめることができるものよりも、一般的に使用が便利で、エラー処理もはるかに優れています。
ただし、DLLはパックファイルの透過的な解凍と、各ゲームプレイインスタンスの一部として管理されるECL、SCL、およびリプレイバッファの割り当てを担当します。実際、これこそが3つのWin32ヒープ管理関数が登場する理由です。これにより、リプレイの初期化は、ENEMY.DATとリプレイファイルの生のコンテンツをC_SSGインスタンスに借りたバッファとして渡し、ステージ番号を渡して必要なスクリプトをロードする短いステップのシーケンスに分解されます。しかし、jwilinsの生の資産ロードフォークも存在し、実験的なユースケースでそれを支持しているため、パックファイルをバイパスして生のECLおよびSCLバッファを受け取るバリアントも追加しました。 -
FFIの使用と言えば、これはこのライブラリのもう1つの主要な設計目標でした。結局、最終的にはこのコードをPythonで書かれたSilent Seleneのサーバーでリプレイ検証を実行したいと考えています。また、リプレイをPostgreSQLデータベースに保存するため、リプレイをファイルシステムに書き出す前にゲームロジックコードに渡す必要がなくなります。
特定の開発されたユースケースがない限り、現時点ではFFIの完全な機能同等性を費やすことはあまり意味がありませんでした。そして、現時点では、リプレイの実行と検査はC++限定です。しかし、少なくともビルド時に、意図的に公開されたすべてのヘッダーとpbgの元のゲームプレイ構造が有効なCコードであることを確認しました。これにより、あらゆる種類の自動バインディングジェネレーターにそれらをフィードできます。 -
ほとんどの場合、ロジックとプレゼンテーションの分離は、元のコードの
GameMove()とGameDraw()の間の分割と同様に機能します。ロジックレイヤーは1フレームのゲームプレイを実行し、フロントエンドのレンダラーは結果の状態を取得して描画できます。しかし、フレームの途中で発生する可能性のあるイベントはまだいくつかあり、フレーム終了時にゲーム状態から取得できない条件とパラメータによってトリガーされます。サウンドエフェクトは最も明白な例ですが、これらの種類のイベントは、SCL命令の大部分、すべての種類の背景効果、およびゲーム内ダイアログシステムの全体を含めて発生する必要があります。これらのイベントをすべてフレーム終了時に処理するためのキューに入れることもできましたが、それには割り当てが必要になり、元のゲームの動作と完全に一致するように、イベント発生時に大量のデータをスナップショットする必要があるというまったく新しい問題が生じます。したがって、C関数ポインタを介したクラシックなフックが採用されました。
特定のビジュアルエフェクトのために3つの特定のECL命令をカバーする必要があり、理想的にはpbgの既存のデバッガをカバーする必要があるため、命令ごとのECLおよびSCLフックを完全に採用しました。理論的には、これらのフックを使用して、ゲームロジックライブラリの上に完全なデバッガを構築することさえできます。ただし、ECLの場合、すべての命令のフックを受け取ることを明示的にオプトインする必要があります。Extra Stageは7分間のフルラン全体で約20万のECL命令を処理するため、C関数ポインタが高価なFFI境界を越える必要がある一部の言語にとっては、あまり良い兆候ではありません。SCL命令は、はるかに数が少ないため、無条件にフックされます。 -
そして、どこにもうまく収まらないので:明らかに、Linuxバージョンにも
libssg.so.1.v1.005があり、通常の共有ライブラリの規則に従って、バージョンなしのリンカ名とバージョン付きSONAMEバリアントがあります。通常、後者の2つは、実際のコードを含む実際の共有オブジェクトを最終的に指すシンボリックリンクチェーンを形成します。しかし残念ながら、この単純なアプローチは、特にTupでのビルドパフォーマンスを大幅に低下させます。TupのUnix中心の起源にもかかわらず、Tupfilesは、DLLのAPIサーフェスを定義するインポート.libファイルが別にあるWindowsアプローチを強く好みます。次に、DLLのリンクルールで^o^フラグを使用し、そのDLLの新しいバージョンをビルドすると、Tupは新しく作成されたインポート.libが古いものと完全に同一である場合、依存するバイナリの不要な再リンクをスキップできます。しかしLinuxでは、libssg.so(リンカが-lssgを指定したときに読み取るもの)の背後にある最終ターゲットは、コード変更ごとに基本的に異なるため、依存するすべてのバイナリの再ビルドがトリガーされ、共有ライブラリの大きな利点が無効になります。
UnixリンカはWindows .libファイルと直接同等のものを生成しませんが、通常のフル.soファイルからコードレス.soダミーの.lib相当物を派生させることは可能です。必要なのは、nmとsedの魔法で、通常の非シンボリックリンクlibssg.soにリンクできる安定したASMソースを作成することだけです。🧙 次に、リンカ(そしてリンカのみ)は実際のコードを含むフル.soファイルを実際に見ることはなくなり、Linuxでも同じ開発者エクスペリエンスを再現するために^o^フラグを使用できます。詳細については、対応するtupblocksコミットを確認してください。
1つの隠された変更は、ゲームのデバッグモードに関連しています。pbgの元のコードには、衝突検出を無効にするデバッグオプションがあり、デバッグ専用フラグに基づいて「VIVITがヒットした」関数から条件付きで戻ることで実装されていました。残念ながら、この種のデバッグ機能は、私たちがやろうとしていることに対して、概念的に大きな問題を引き起こします。
- 私たちのライブラリは複数のゲーム状態を並列で処理できる必要があるため、これはもはやグローバルフラグであってはなりません。CLIおよびロジックAPIユーザーは、フラグが設定されていたかどうかに関係なく、リプレイを2つのバッチに分割する必要はありません。したがって、これは別のラウンドパラメータでなければなりません。
- しかし、これが適切なラウンドパラメータではなかったということは、フラグがpbgの古いリプレイフォーマットにまったく伝播されなかったことを意味します。したがって、ヘッダーデータだけで
Hitオプションが[O F F]に設定されて記録された既存のリプレイを特定することはすでに不可能です。CLIはそのようなリプレイの特定を支援します(衝突検出を有効にして実行すると、どこかの時点で同期ずれが発生する可能性が高いため)が、断定することはできません。 - しかし、リプレイにこのフラグが含まれていない場合、誰がそれを設定するのでしょうか? まあ、それは引き続きフロントエンドから渡されるでしょう。つまり、ゲームとリプレイCLIであり、後者にさらにコマンドラインオプションが追加されます。そして、私たちは元の場所に戻り、ユーザーにシミュレーション環境の一部としてリプレイ固有のプロパティを手動で追跡するように強制します。
- 今からそのフラグを追跡し始め、pbgのリプレイヘッダーの未使用ビットの1つを再利用することができます。しかし、そのようなラウンドパラメータを最初に持つことで、チーターへの扉を開くべきでしょうか?
- デバッグビルドにこの機能を制限し続けることはできます。しかし、デバッグビルドとリリースビルドの間で別々の動作を持ち、さらに別々のAPI契約を追加したいのでしょうか? 😵
これらはすべて、非開発者が決して遭遇しないであろう些細なデバッグ機能にとっては、答えが簡単ではない質問が多すぎます。削除するのが最も簡単な方法のように思えますが、一方で、この種の機能はデバッグに不可欠です。[画像: :thonk:]
したがって、ゲームロジックに影響を与えない別の無敵状態のアプローチに置き換えることにしました。各ラウンドを252ライフで開始するだけです。なぜ252なのか、8ビット符号なし整数から期待される255ではなく、当然それに割り当てられるからです。ゲームは6ステージ全体で3つのエクステンドアイテムをスポーンさせ、pbgはライフを最大値にクランプしません。252ライフは、通常の6ステージランとExtraステージの両方を移動せずにクリアするのに十分であり、ライフカウンターが0にロールオーバーすることなく、すべての3つのエクステンドを回収するノーミスランを実行することもできます。
この新しい機能は単に初期のPlayerStock値をオーバーライドするだけなので、これはリプレイに直接保存されます。これにより、はるかに持続可能なデバッグ機能が得られます。この機能が有効な状態で記録された新しいデバッグリプレイは、秋霜玉のpbgの元のリリースでさえ互換性があり、リプレイホスターは、古いフラグを示すために未使用のビットを再利用した場合と同じように簡単に識別できます。おそらく、PlayerStock番号はUIが目立つように表示できる通常の構成オプションであるため、確認する必要さえありません。
新しい拡張可能なリプレイフォーマットが利用可能になり、要求の多いクワークトグルに必要なすべての官僚手続きを実装したら、元の機能を復元できるかもしれません。それだけでも、少なくとも1つのリプレイ非互換のゲームプレイフォークが作成されるでしょう。しかし、現時点では、これが私たちが進んでいる方法です。
[画像: pbgのデバッグモードの秋霜玉の設定→難易度メニューのスクリーンショット。PBG_DEBUGプリプロセッサマクロを定義することでアクティブ化され、pbgの元のコードとP0326までのすべてのReC98ビルドに示されています。カーソルは、メニュー内の同じスロットを占めていた元のヒットオプションを無効にする[O F F]に設定されたヒットオプションを強調表示します。][画像: ReC98 P0341ビルドで、PBG_DEBUGプリプロセッサマクロを定義することでアクティブ化された、pbgのデバッグモードの秋霜玉の設定→難易度メニューのスクリーンショット。カーソルは、元のヒットオプションを置き換える[O N]に設定された新しい252Livesオプションを強調表示します。]
メニューロジックの反転に注意してください。元のHit機能では、[O F F]の選択は非標準のゲームプレイをオプトインしましたが、252Lives機能は[O N]に設定されたときにのみPlayerStockをオーバーライドします。否定は純粋に視覚的なものであり、既存の秋霜DBG.DATファイルは自動的に一致する動作を使用します。
コードベースが以前よりも2つの追加バイナリを生成する必要があるため、リポジトリの物理レイアウトと、これらのバイナリ間で共有する必要があるコードをどのように共有するのが最善かを再考する必要がありました。秋霜玉の開発に着手して以来、汎用エンジンとプラットフォームコードをゲーム固有のコードから物理的に分離することに意識的に努めてきました。当初、ビルドシステムからの最も論理的で簡潔なレイアウトを選択しました。platform/ディレクトリのさまざまなサブディレクトリには、すべてのプラットフォーム固有のバックエンドコードが含まれており、game/にはそれ以外のすべてのコードが含まれていました。これにより、Tupfileはシンプルに保たれました。プラットフォームのそれぞれのバイナリに入れるべきすべてのファイルを表示するために、いくつかの特定のグロブしか必要ありませんでした。長年にわたり、いくつかのひび割れと例外が出現しましたが、全体的なレイアウトのアイデアはうまく機能しました…
…しかし、すべてのコードが1つのバイナリにしか入らない限り、何をやっても関係ありませんでした。しかし、今では3つ追加するので、すべてのコードを3つのバイナリすべてにリンクし、不要なものをリンク解除するためにリンカに依存したくありません。これらの不要な依存関係は、Tupのスループットを低下させ、開発中に不要な再ビルドをトリガーする、やや三角形に見えるビルドグラフを形成することになります。そして、リプレイサーバーに、ゲームロジック.soバイナリをコンパイルするだけでよい場合に、これらの開発パッケージすべてをインストールするように強制したくありません。
そのため、完成した機能は、ゲームに依存しないコードを合計4つの異なる軸に沿ってモジュール化することを要求しました。
- C++標準ライブラリモジュールの上に少量のコードを提供し、eyalrozのprintfライブラリを追加し、さらに依存関係を追加しないロジックレイヤー。主に型、クラス、テンプレートを定義し、それ自体で少数の翻訳ユニットで構成されているため、3つのバイナリすべてに静的にリンクするのに十分小さいです。
- ゲームとリプレイCLIの両方で使用したいSDL上のヘルパーと、SDL依存性自体を含むアプリレイヤー。
- APIレイヤー。これは現在、ゲームロジックとReC98ビルドバージョン文字列を取得する関数のみを含んでいます。これは、ゲームロジックDLLにリンクされるが、CLIまたはゲームバイナリにはリンクされない追加のゲームに依存しないレイヤーが必要だったためです。なぜなら、それらは常にDLLからこれらのバージョンを取得する必要があるからです。
- そして最後に、メインのオーディオビジュアルゲーム体験に必要な残りのコードすべてをバンドルするエンジンレイヤー。このレイヤーは、libogg/libvorbis、dr_flac、libwebp、miniaudioなどの残りのサードパーティ依存関係もバンドルします。
または、表形式で表示すると次のようになります。
| レイヤー | ロジックDLL | リプレイCLI | ゲームバイナリ |
|---|---|---|---|
| logic/ | ✔️ | ✔️ | ✔️ |
| app/ | ✔️ | ✔️ | |
| api/ | ✔️ | ||
| engine/ | ✔️ |
そして、一歩引いて、実際に何を書いたかを見ると、このコードのほとんどは秋霜玉固有ではなく、将来のKioh GyokuやPC-98東方の移植にも適用できることに気づきます。
- ウィンドウモードと排他/ボーダーレスフルスクリーンモード、ゲーム内から自由に切り替え可能? ✅ チェック。このロジックを二度と書きたくありません。
- ハードウェアスケーリングによるソフトウェアレンダリング? ✅ 📝 チェック。
- 線、三角形、四角形の描画? ✅ Kioh Gyokuには明らかに役立ちますが、このAPIはPC-98東方のネイティブポートのmaster.libの代替になる可能性があります。
- スプライト管理とブリッティング? ✅ 同上。
- TTF/OTFテキストレンダリング? PC-98東方のフォントROMダンプの代わりに、MSゴシック、IPAMonaGothic、またはUnifontの埋め込み16ピクセルビットマップが必要になるかどうかはわかりませんが、✅ Kioh Gyokuには間違いなく必要になります。実際、📝 この現在のアーキテクチャ全体をKioh Gyokuを念頭に置いて構築しました。コードとその依存関係を条件付きで無効にすることは、さらに再利用可能なモジュールに分割するよりも理にかなっています。
- スクリーンショット? ✅ 📝 ロスレスWebPはPC-98東方にとって引き続き正しい選択でしょう。
- サウンド? ✅ 📝 必須のYM2608エミュレータは、クロスプラットフォームオーディオAPIにサンプルを送信する必要があります。
- 波形トラックによるBGMモディング? ✅ 誰かが遅かれ早かれそれを求めるでしょう。
- リプレイCLI? ✅ 少なくとも汎用CLIコードに関しては。
- バッファ所有権、📝 整数型キャスト、エンディアンネス、Shift-JISとUTF-8文字列間の型レベルの区別など、バッファ所有権を扱う小さなロジックコードの断片はすべて? ✅ レガシーコードベースにとって正しいアプローチであると確信していなければ書きませんでした。そして、それらはすべてこの一連のゲームに適用されます。
今や、これらの機能の合計は、日本のレトロシューティングゲームのモダンシステム移植のための実績あるソリューションと、品質向上機能を提供する、それ自体がライブラリになっています。そして、私たちがここで移植したい7つのゲームの非常に具体的なスコープを考えると、この新しいライブラリにHatoyamaという名前を付けるのに、これ以上の名前があるでしょうか。SeihouとPC-98東方の両方をカバーし、元の時代の誠実で、今回は皮肉抜きの同人精神を呼び起こします。🕊️
…OK、そして Torte le Magicも、もしそれがオープンソース化されたら。それでも適切な名前です。
この新しく分離されたライブラリにより、資金調達に関しては少しトリックを仕掛けることができました。Hatoyamaの現在の作業はSeihouに必要かもしれませんが、Seihouに特化しているわけではありません。なぜなら、それは最終的なPC-98東方のモダンシステム移植にも、それ以上に、利益をもたらすからです。したがって、Hatoyamaは、そのバックアップが明示的にSeihou関連の作業にオプトインしていなくても、Anything予算を消費できます。実際、この分割は、この配信に費やされた作業の約1/4を資金調達するのにすでに非常に必要であることが判明しました。
これにより、PC-98東方の移植作業の価値も提供したことになります。一部の人々がまだPC-98東方の移植はまだ始まっていないと考えているとしてもです。
そして、CLI引数解析コードの数百行の後、ついに…
リプレイ検証CLI
Usage: ssg_cli [OPTIONS] replay_file [replay_file...]
Options:
-d, --dat <ENEMY.DAT> カスタムパスゲームデータアーカイブ
-h, --help この使用法テキストを表示
-q, --quiet 完全にシミュレートされないリプレイのみを報告
-v, --version バージョン情報を表示
`replay_file`は`?`と`*`ワイルドカードを含めることができます。
`-d`/`--dat`が省略された場合、ゲームデータは`C:\path\to\ssg\ENEMY.DAT`から取得されます。
はい、これはすべてのオペレーティングシステムで単一レベルのグロビングです。相対パス、絶対パス、およびUNCネットワークパスで動作します!
このようなCLIでリプレイを正しく初期化することは、最後のわずかな課題でした。pbgの単一ステージリプレイフォーマットは、ヘッダーにステージを実際に保存しないため、ファイル名からのみリプレイのステージを推測できます。元のゲームのリプレイメニューが期待するのと同じ秋霜りぷ*.DATというファイル名スキームを強制することもできましたが、そのアイデアは、現在の秋霜玉リプレイコレクションの複数ステージの説明的な名前のリプレイでさえ、すぐに崩壊しました。明らかに、単一の*グロブでセット全体を分析したいです。これらのリプレイをゲーム内で表示するために説明的なテキストをトリミングする必要があることを念頭に置くと、リネームを容易にするために変更しない部分のみを考慮する小さなアルゴリズムを採用するのが理にかなっていました。そして、結果のアルゴリズムは、CP1252で得られる‘š‚è‚Õ文字列、およびWestern Windows 98で得られる4つのアンダースコア(____)を含む、そのプレフィックスのいくつかのモジバケバージョンを探します。
このUnicodeファイル名を処理する必要があることも、典型的な奇妙なReC98エンジニアリングの決定の1つと思われるものにつながります。app/レイヤーのSDLへの依存性は、このCLIをSDLの標準ライブラリとロギング関数の上に完全に構築することを選択したことをすでに示唆しているかもしれません。通常、CLIツールに低レベルのゲームライブラリを使用することは検討しませんが、特にSDLにはいくつかの良い理由があります。
-
Unicodeコマンドライン引数は、プラットフォーム固有のコードを一行も書かずに機能します。このツールが通常処理する必要のあるファイル名を考えると、この機能は明らかに重要です。プラットフォーム固有のコードを再度書く必要がないのは素晴らしいことです。同じ問題をすべて再度解決する必要がありません。
-
WindowsでのUnicodeコンソール出力についても同様です。実際に必要なプラットフォーム固有のコードは、SDLの
stderrへの出力という残念な選択から来ており、stdoutではなく、いくつかのやや厄介なコード行が必要です。 -
SDL_GlobDirectory()のおかげで、強力なシェルを持たないシステム向けのクロスプラットフォームグロビングサポートは、迅速かつ安価に追加されました。誰かがPRを送信するとすぐに、**を介した再帰グロビングが得られるかもしれません。パスとファイル名のパラメータの強制的な分離は、この関数の使用を少し複雑にしますが、それほど悪くはありません。間違いなく、C++標準ライブラリに固執するよりもはるかに簡単です。それにはグロブを正規表現に変換してすべてのファイルを手動で一致させる必要があります。 -
SDLは、WindowsとLinuxのパッケージにすべて含まれています。
-
最終的に、このCLIは2つの目標を達成することを目的としています。秋霜玉のゲームロジックにヘッドレスAPIがあることの一般的なデモンストレーション、そして私のリリースパイプラインの一部として、最終的に記録されたすべてのリプレイの一括検証を容易にするツールです。検証のアプローチによっては、より機械可読な出力フォーマットのサポートを追加する場合があります。いずれにせよ、CLIはAPIをデモンストレーションすることを目的としており、その逆ではありません。
-
引数解析とグロビングコードをすべて削除すると、DLLのAPIを呼び出して
ENEMY.DATとリプレイをロードし、各フレームをループする約60行のC++コードが残ります。そして、これはすべてのステップでのエラー処理を含んでいます。CLIにシェルアウトしてその出力を解析するよりも、プログラミング言語の選択肢から同じC APIを呼び出す方がコードが多くかかる可能性があります。そして、このAPIを最も基本的なステップにまで煮詰めるのに費やされた数か月の作業を考えると、かなりの間安定したままでしょう。
(フックは間違いなく増えるでしょう。必要に応じて、選択した言語用の適切なバインディングジェネレーターを使用してください!) -
CLIのほぼすべてがSDLまたはロジックDLL関数の呼び出しであるため、WindowsでC++ランタイムの初期化を、再び、わずかな追加コストで削除でき、9 KiBのバイナリが得られます! これらの光学系に過度に重要性を置いているかもしれませんが、CLIには追加の魔法は一切含まれていないことを明確に伝える方法が気に入っています。ソースコードを読まなくてもそうです。
さて、すべてをきれいに最小限に抑えましたが、レンダリングする必要がない場合、実際にはどれくらいの速さでゲームプレイをシミュレートできるでしょうか?
秋霜りぷ4 (Laser).DAT:
Stage cleared: Yes
Frames: 10842
Score: 2387540
Misses: 4
Bombs: 10
Seed: 1e690b73
Load time: 0.5 ms
Simulation time: 47.6 ms
Simulation speed: 227773 FPS
秋霜りぷ6 (Laser).DAT:
Stage cleared: Yes
Frames: 13460
Score: 28324900
Misses: 5
Bombs: 12
Seed: 0243067c
Load time: 0.5 ms
Simulation time: 77.8 ms
Simulation speed: 173008 FPS
秋霜りぷEx.DAT(ワイドショット):
Stage cleared: Yes
Frames: 27122
Score: 80983880
Misses: 3
Bombs: 6
Seed: 0176d0d8
Load time: 0.5 ms
Simulation time: 151.6 ms
Simulation speed: 178905 FPS
秋霜りぷEx(霊夢第二次形態の攻撃を全て回避).DAT:
Stage cleared: Yes
Frames: 26212
Score: 114190200
Misses: 4
Bombs: 10
Seed: 03610cb9
Load time: 0.5 ms
Simulation time: 179.3 ms
Simulation speed: 146191 FPS
WARNING: `秋霜りぷ3 (Full clear, Hit OFF).DAT`: simulation stopped after 2035/15556 frames (13.1%)
Score: 6200
Misses: 5
Bombs: 0
Seed: 1f0a25b1
Load time: 0.7 ms
Simulation time: 6.1 ms
Simulation speed: 333607 FPS
いつものように、パフォーマンスは私の古いIntel Core i5-8400Tで実行されている32ビットWindowsビルドに対応しています。他の人からはパフォーマンスの数値は提供されませんでした。
はい。これは、古いミッドレンジCPUで200ミリ秒未満でシミュレートされた7分間のExtra Stageゲームプレイです。MSVCのひどいコード生成に苦しみ、まだいくつかの未開発の最適化機会があるシングルスレッド32ビットコードを実行しています。ゲームロジックがこの速度の20%で実行されれば満足していたでしょう。しかし、これらの数値があれば、これらの非常に詰め込まれた6回のプッシュからマルチスレッドを自信を持ってカットし、実際に大量の記録済みリプレイを検証するまで延期できます。
さらに良くなります。私が話しているssg.v1.005.dllは、通常のモダンビルドだけでなく、Windows 98ビルドでも使用されています。したがって、i586時代のx86命令のみを使用して、このレベルのパフォーマンスを達成しました。クリーンなゲームディレクトリのためにパフォーマンスを犠牲にするという考え自体に反対するかもしれませんが、それは:
| リプレイ | 32ビット、i586 | 32ビット、SSE2 | 64ビット、-march=x86_64 |
|---|---|---|---|
| ステージ4 (レーザー) | 46.2 ms 47.4 ms 50.2 ms | 43.6 ms 44.6 ms 46.5 ms | 43.0 ms 44.4 ms 45.9 ms |
| ステージ6 (レーザー) | 73.1 ms 75.1 ms 78.2 ms | 69.6 ms 71.5 ms 75.1 ms | 66.9 ms 68.7 ms 72.9 ms |
| Extra Stage (ワイド) | 145.5 ms 149.3 ms 153.0 ms | 138.5 ms 143.1 ms 148.5 ms | 133.9 ms 136.8 ms 146.6 ms |
| Extra Stage (ホーミング) | 178.6 ms 182.5 ms 189.1 ms | 171.5 ms 174.6 ms 181.3 ms | 157.0 ms 161.8 ms 168.5 ms |
同じシステムで30回の実行のうち、キャッシュがウォームアップされた状態での最速、中央値、および最遅のシミュレーション速度。
はい、-march=nativeは存在しますが、私の古いCPUでは2%遅くなります。コンピューターは時々奇妙です。
ビルドを複雑にし、約5%の速度向上のために別のファイルを追加するのはなぜですか?通常の64ビットLinuxビルド(2003年のベースラインx86-64 ISAをターゲット)は、すでに約7%から約13%高速に実行されます。そして、もしこれが結局遅すぎることが判明した場合、個々の命令を凝視するよりも、アルゴリズムの最適化を探求する方が良いでしょう。
ダウンロードリンク? ダウンロードリンク:
念のため、これらの特定のリプレイでCLIをベンチマークしたい場合は:2026-10-05-SH01-benchmark-replays.zip
今後の作業
Hatoyamaの分割があっても、受け取った資金でこの機能を完全にリリースするために、いくつかのマイナーな妥協をしなければなりませんでした。
-
パックファイル圧縮(秋霜りぷ*.DATリプレイフォーマットを含む)はロジックDLLの一部にすることができます。これは現在、サードパーティの再パッキングツールなしでの手続き的なリプレイ生成を妨げています。
-
スワップ可能なゲームロジックレイヤー。理想的には、モッダーはユニークな名前のロジックDLLを共有することでゲームプレイMODを配布できるはずです。プレイヤーはメニューで選択するか、CLIパラメータを指定して選択できるはずです。異なるロジックDLLを使用するために、ゲームまたはCLIを再リンクする必要はありません。
-
ロジックDLLの生成コードは、部分的にひどすぎるため、WindowsビルドをMinGWに切り替えることを真剣に検討しています。特に、最近のGCCバージョンで古いWindowsバージョンをターゲットにすることに焦点を当てた非常に優れたフォークがあるためです。これは、クラシックなMinGWパッケージングの罠を回避することに同意した責任ある開発者によってもメンテナンスされています。📝 現在のVisual StudioでWindows 98サポートを追跡しようとする戦略を放棄する場合、Windows 98ビルドのコストを大幅に削減できる可能性があります。(そしてついに、あの肥大化したものをアンインストール!)
-
もし単に私に好意をしてくれるなら:miniaudioから離れる時が来ました。😩 SDL 3のオーディオAPIは、モダンビルドの肥大化とコンパイルの痛みを軽減し、Windows 98ビルドを生のDirectSoundに戻し、波形BGMのサポートをバックポートすることで、特にVMでのオーディオ遅延を大幅に削減します。
-
最後に:これはKioh Gyokuに取り掛かるのに最適な時点でしょう。過去数年間の秋霜玉のクロスプラットフォームエンジン作業と、このリリースで行ったすべてのコード分割により、それを2番目のゲームに適用することはかなり迅速に行えるはずです。本当に、このゲームを詳しく見れば見るほど、それに取り組むことに興奮します。秋霜玉の対応するコードと同一のコード、または移植方法を正確に知っているコード、あるいは移植する必要のないコードがたくさんあります。もちろん、それ自身の課題もありますが、コードベースは一見したところはるかにきれいで、秋霜玉が苦しむひどい8ビットレンダリングとリプレイ保存の制約にも苦しんでいません。秋霜玉を移植した時間の数分の一の時間でこのゲームを移植できると確信しています。
次に:TH02のデコンパイルに数回のプッシュを費やします! プレイヤーショットから始めようと思います。それはかなりのコード量であり、それほど時間はかからず、RE%とPI%の両方を大幅に押し上げるでしょう。そして、私たちは確かに大きな利益を享受するはずです。最も注目すべきは、ゲームコミュニティがしばらくの間疑問に思っていたある謎を解決することです。
しかし、ゲームコミュニティに何の恩恵も与えるべきではありません。なぜなら、彼らはReC98のコミッションについて無限にミームを作成するだけで、実際にはもう行わないからです…
…待ってください。このリリースが、合理的な期間内にリリースされなかったことに気づいたかもしれません。📝 去年の大きなサブプロジェクトはほぼ2倍の複雑さでしたが、はるかに速く完了しました。では、何が起こったのでしょうか?
-
3月下旬、その後何もありません。ストアはまだ広く開いていたにもかかわらず。新しい配信の直後にストアが完売した頃から、私たちは本当に長い道のりを歩んできました。現在のサブスクリプションボリュームに合わせたレベルに開発を遅らせ、📝 前回のリリースの後、必要な休息を取りました。
-
4月または5月にも新しいトランザクションはなく、一般的な経済的懸念を示唆しています。それでも大丈夫です、キャップシステムは設計どおりに機能しています。開発が遅いため、5月にはわずかにペースを上げて作業を続けました。秋霜玉のユニークな✨制約✨とそれに伴う将来を見据えた計画は、その月中の迅速な進歩、ましてやリリースへの希望を絞め続けるでしょう。
-
ストアは、3月以来変更されていないサブスクリプションボリュームが6月初旬にキャップを満たした後、自然に閉じました。人々は後に、私が常に完売していたと主張するために、この完売状態を利用して、資金不足が問題であったという提案を却下するでしょう。しかし、開発がしばらく遅かったために完売したという事実を完全に無視してです。
-
TH06 New Classic が突然発表されました。このリリースに関与する非常に日本的なビジネス階層の誰かが、コミュニティのデコンパイルにまったく問題がなく、少なくとも何らかの形で公式リリースに使用する可能性があることを強く示唆しています。十分な人々がそれについて知っており、事前に教えてくれた可能性があり、おそらく急いで、古い東方への高まるコミュニティの関心に備えるために容量を解放できたかもしれません。彼らはそうしましたか?いいえ、もちろん、私は誰だと思いますか?全体像において重要な人物ですか? はい、いいえ、16年間のインフラ作業は、せいぜいフリンジの奇妙な人としてしか分類されません。Nintendo® Direct™でのゲーム発表の方が、支払っているファンの群衆に心理的に意図されたハイプ効果を与えるにははるかに重要です。
コミュニティ主導であることを誇る「同人」ゲームシリーズのNDA、想像してみてください。どれほど完全に嫌悪感があるか。
まあ、今急がなければなりません。 -
6月に、ある開発者が私の秋霜玉コードベースをフォークし、
masterの上にバイブコードを適用したことに気づきました。フォークは貴重なQoL機能をゲームプレイ関連の変更とワイルドに混合しており、リプレイの互換性を明らかに壊しています。表面上、それはすべて、通常バイブコードに関連付けられるスロップの正確な合計です。そして、私の細心の注意を払った作業に競争を提供するものではありません。実際、READMEの最初の文からスロップは明らかです。
Project Shrine Maiden (pbg) による最初の東方Project関連ゲーム、秋霜玉のソースコード。
それでも、これらのバイブコーダーの操作速度を心配せずにはいられません。もう少し規律を持ってプロンプトし、特定のことに焦点を当てていれば…
-
スーパープレイヤーであり、AIの人でもある人物が私に連絡を取り、ReC98ビルド環境の設定を手伝ってほしいと頼み、次に貢献アイデアのリストを求めました。1日で2つ実装しました。コードの膨大な量はAIを強く示唆しています。私は今、この作業をレビューし、おそらくアンスロップする必要があるでしょう。今は違いますが、私の脳は別の場所にあり、少なくともこの秋霜玉機能を完成させたいです。
-
数日後、同じ人物のReC98フォークには数百のデコンパイトコミットを含むブランチがあります。その人物は、洞察を利用して、かつては私の仕事専用だったレベルで、これまで知られていなかったゲームプレイの詳細を理解し始めました。ゲームコミュニティは当然祝っています。私は、深刻な競合相手、または完全に事業から撤退する危険性からわずか1回の公開発表で離れていることに気づきました。なぜなら、この人物はすべて無料で行っているからです。
-
機能開発は数日後に最終的に完了しました。ゲームロジックDLLに関するツイートは平均的に好評でした。あるいは、その乾燥した技術的な内容を考えると、驚くほど好評でした。おそらく、若いGen ZのWindows 9xスクリーンショットのロマンチック化などのせいです。しかし、この分割の実際に有用な結果をほとんど誰も気にしませんでした。そして、それはソーシャルメディアアルゴリズムの問題でもありません。ビューは他のツイートからそれほど離れておらず、すべてのいいねは開発者であることを知っている人から来ました。ゲーマーは、彼らの信頼を得て確保するために特別に作られた機能には関心がありませんでした。
-
しかし、待ってください、1つの返信がありました。
おい、これはいいね、そして君が終わるのを待つよ、でもFreeBSDは史上最高のオペレーティングシステムだ! そして、私は秋霜玉をそれにコンパイルしたい (アーカイブリンク) > 君のビルドシステムは悪い。なぜなら、私が人生で他にすることのないものを選んだ、最も愚かな*nixシステムであるという特別なニーズに適していないからだ (アーカイブリンク) > 私は私のニッチなOSの好みを建設的に使用して、そのコアにあるビルド問題の背後にある構造的な問題を修正するのではなく、ランダムなメンテナーを過激な回避策を提案して悩ませるだろう (アーカイブリンク) > プロジェクトはLinuxismを避けるべきだ☝️👮
Yup。パフォーマンス測定が異なるハードウェアで提供されず、トピックへの実質的な返信もない。いいえ、FreeBSD。ゲームコミュニティは私の首に息を吹きかけており、あるBSD野郎が私の注意を最も愚かないじくり回しのくだらないものに向け、同時に、私がCMakeについてどう考えているかを説明するブログ記事を読んだことがないかのように聞こえます。なぜなら、私たちは今、デスクトップで市場シェアが約0%のOSをサポートしなければならないからです。https://gs.statcounter.com/os-market-share/desktop/worldwideそして、そのメンテナーは自分たちの行動をまとめられず、ふりをしたり、2年半前にC++で標準化された機能が重要ではないと偽ったりするからです。私は、これほど意図的に鈍感なLinuxディストリビューションを見たことがありません。libc++を通常どおりにビルドし、おそらくいくつかのファイルをディストリビューション固有の場所に移動するだけで済みます。Flatpakでまったく同じことをしましたが、問題ありませんでした。「常識」も「Linuxism」なのでしょうか?
以前、Windowsismをコンピューティングの最大の課題と呼んだのと同じ人物からのものです。わかりません、私は世界で最も安定したABIをターゲットにした美しいバイナリを作成して出荷する芸術を楽しんでいますが、そこには一理あると思います。ただ、今では、目標が移動しすぎて、Linuxカーネルでさえ合意できないところまで来ています。フォントを正しく設定しませんか?ディストロホッピングして、あなたが来る前に存在しなかった問題を作成するのではなく?!人々の優先順位はどうなっているのですか?!Windowsが悪魔であると考えて、過去30年間Windowsが存続してきた理由を証明するような行動をとる…
ああ、そしてサウンドも壊れています。なぜなら、FreeBSDもminiaudioの主要なターゲットではないからです? 不思議ですね。 🙄
この問題を通常のプッシュまたは2でFreeBSDに移植できたらよかったのにと思います。私のシステムが問題なく動作するのを妨げているサプライチェーンパッケージングの問題に適切に対処し、解決し、そして皆のためにFreeBSDをより良くすること。BSDアプローチが一部の場所でより理にかなっている可能性がある理由を調査すること。なぜなら、それはどこにも文書化されていないからです。
それどころか、誰かがその作業を行い、レビューしやすい十分に議論されたPRを送信し、変更に関する有用なコンテキストを提供してくれたとしても、それは問題ありませんでした。しかし、このほどんど無関係なもののために、あなたが想像できる限り最も当然の方法で指を振り上げた後、QAの真っ只中に半端なPRを投げつけること。これは、それ自体のためだけの熱狂です。あなたが決して私の近くに再び現れないことを願っています。
グローバルOS採用の問題は、くだらないニッチインディゲームのメンテナーの負担で戦われるものではありません。デスクトップでのBSDの低い採用率が誰にとっても解決する価値のある問題であるという単純な提案は、私がコンピューターを完全にやめて、残りの人生を農業やパン屋を経営するなど、世界に何か価値を提供するようなことをしたいと思わせます。ええ、それは何か価値を提供するでしょう。OSについてサークルジェークしているspaztron64に加わってください。彼はすでにこのばかげたことを称賛しました。
私は、私が今考えていることを大声で言えるようになったので、それは素晴らしいことです。❤️
-
まあ、QAに戻りましょう。並行して、📝 人生の優先順位の変化は悪化し、8月初旬までに完全に逆転しました。QA作業は、スコープがエスカレートするにつれて過去数年間ますます苦痛になり、現在では、激しいストレス、日中の仕事の退屈さ、コミュニティの現在の機能への無関心、バイブコーダーの差し迫った脅威(それらの気晴らしによっていくらか抑制されている)、そしてある子供が好きな子供のために新しいリポジトリレイアウトをリファクタリングする必要があるという背景に対して、この作業を行う必要があります。その結果、その月のほとんどまたはまったく何もできませんでした。
-
ゲームコミュニティのDiscordサーバーの別の誰かが私の秋霜玉フォークをフォークし、より良いリプレイを実装しました。待て、何だと?!
masterブランチの上に、新しい完成したばかりの機能で、既存の議論に参加することなく、既存の議論に参加することなく? 結果は表面上バイブコードのように見えませんが、深く調べる気はありません。
おそらくそれは単なる娯楽の練習であり、コミュニティ全体の標準を設定することを意図したものではありません。おそらくそうではありませんが、開発者は真剣に見えます。ゲームコミュニティは、私がすべてを準備していたこと、そして彼らが準備を手伝ってくれたことを完全に忘れたかのように、それを称賛します。誰も質問をしません。
これで、1か月で私のビジネスモデルを回避し、損なうことを試みた人が3人になりました。 -
6月30日、月刊加入者感謝ツイートの機会を利用して、バイブコーダー状況がクレイジーで脅威的であることを強調しました。誰も気にしません。後でそのツイートをスクロールダウンして、私が想像していたのではないと気づく人はごくわずかでしょう。多分。
-
明らかに、このままでは続けられません。少なくとも1つのストレス源を排除する必要があります。
古い東方へのこの高まった関心を利用できるはずですよね? 再度宣伝し、人々と話し、交渉する。人生の優先順位をReC98に戻し、新たな決意を持って、なぜなら他の優先順位がうまくいかなかったことが判明したからです。数人によって裏付けられた、約またはまでのサブスクリプションボリュームを確保します。そして、これがどのように機能するかを確認するために、1年間だけ日中の仕事を辞めることができます。同僚も上司とまったく同じような手配をしました。全体的な金銭的損失はありますが、十分な貯蓄があるので、数年間は飢えずに済みます。そして、増加した速度と焦点は、私の健康とこのプロジェクトの長期目標の両方への価値ある投資になるかもしれません。さらに、より多くのサポートを引き付け、それほど大きな損失を被らないかもしれません。リスクを冒して見つける必要があります。 -
私は、その申請フォームに文字関連の質問があったため、自分で決して参加しなかった、あの大きなPC-98東方Discordサーバーに招待されました。そして、最初の印象が正確であり、サーバーが主にハイパーアクティブな子供たちで構成されていることが判明しました。そこで、私は同じものしか見ません。私はあなたの最大のファンですと言っている人々は、一度もコミッションをしたことがありません。あなたの最大のファンなら、まだ可能だった2.5か月の間に、たとえわずかでもサブスクリプションをこっそり入れていたはずです…
私のサポート基盤は、現時点では何でしょうか? ゲームプレイの人々は口先だけで、誰もが夢の機能に資金を提供していないことにますます不満を抱いています。これらの子供たちは明らかに、彼らが持っているわずかなお金を他のことに使いたいと思っています。残りの数人の加入者は、spaztron64の典型的なクラウドファンディングビジネスモデルへの誹謗中傷が信じられるなら、毎月私にお金を送っていることを忘れているかもしれません。私のブログ記事を楽しむ大多数の人は存在しないかもしれませんが、もし私が引退したら別のブログに移動するでしょう。 -
別のバイブコーダーがGitHubに現れ、
anniversaryブランチを取り、Claudeに186,268行のコードを1日で吐き出させて、SDLベースのTH01ポートをモダンシステムに提供しているようです。計画とテストに重点を置いていることから、この特定のバイブコーダーは非常に真剣であると示唆されています。
現時点では、このポートの作業を継続したり、どこかで宣伝したりしていませんが、それは単なる失敗した実験であったことを示唆しています。もし彼らが真剣な競争相手として自分自身を位置づけないなら、すべては問題ないでしょう。しかし、当時は知りませんでした。また、隠されているバイブコーディングの試みはどうでしょうか? フォークボタンを押さずに、代わりに非公開でポートに取り組んだ人々によって? -
ゲームAIの人々がさらに踏み込み、TH03リプレイをテストスイートとともに実装したと主張し、PIを100%達成し、将来のポートを調査したいと考えています。再び、多くのポグと仲間のゲームプレイの人々からの大きな拍手。spaztron64は、その正当性について単一の質問を提起し、バイブコーディングを疑わしく示唆するが、それを公に認めない返答を得て、拍手に加わります。結局、彼らは配信し、シーンに利益をもたらすので、気に入らないことは何でしょうか?
この展開を数週間見守った後、これは壁に書かれていることです。ゲームプレイコミュニティは、ReC98のチャントが意味のある資金に変わる唯一の残りのグループだったでしょう。しかし、今、そこから誰かがそれを自分でやり始めたので、私ができる意味のあることはもう何も残っておらず、資金も見込めません。
私が受け取ったすべての引退ツイートへの返信の中で、公開されている情報からこれらの部分を自分で接続できた人は一人だけでした。それはtouhou-memoriesでした。spaztron64ではなく、 oh no, my scene clout の主要な柱が脅威にさらされている、彼を説得しよう (アーカイブリンク)のように、彼が他のすべての人と同じようにバイブコーダーを称賛した後、彼が私を言葉で揺るがすことができる立場にあったかのように。そして確かに/jp/ではありません。通常の無知なスラッジで満たされた最初の投稿は、その領域ではいつものことです。しかし、spaztron64について、彼がこのプロジェクトの主要な貢献者であったかのようにスレッドの半分を脱線させることは、彼がこのプロジェクトの近くにいただけでどれだけのクラウドを得たかを示しています。まあ、その混乱は今 settled されたようです。
それ以降、私はこのような環境でReC98を意味のある方法で継続する道はないと見ました。これらのバイブコーダーが互いに話し合い、協力し、それらをリリースし始めると、ReC98はますます誰も支払わなくなるような詐欺のように見え始めるでしょう。
明らかに、あなたは燃え尽きていて、体調が良くないようです。もう少し長い休憩を取ってみてはどうでしょうか?
それがポイントです。この環境では、もう休憩を取ることはできません。サブスクリプションボリュームの低さと単発の寄付の欠如が、4月と5月のように資金状況をキャップからかけ離れたものにしたときに、休憩を取るべきでした。しかし、今日では、すべての休憩は、バイブコーダーが焦り、それを燃やし、プレイヤーにロードマップからさらに別の機能を提供して、ポグ反応を動機付けるためのもう1つの理由にすぎません。コミュニティの分裂や結果は気にしないでください。私が休憩を取ることがまさにこれを引き起こしました! もし私が5月下旬または6月上旬にこれらのプッシュをリリースできたら、おそらくこのエスカレーションは起こらなかったでしょう。
なぜそれらについて人々に話しているのですか? 彼らはそれを隠しておくことを好むのかもしれません。
はい、そうです。リプレイは、退屈だからといって作成するランダムなプレイモッド機能ではありません。この間、jwilinsの生の資産ロードフォークの上に構築された、秋霜玉の静的な英語のみの翻訳パッチもありました。それを脅威と考えましたか? いいえ、それは誰にも害を及ぼさず、私が構築しようとしていたよりプロフェッショナルな多言語翻訳機能とは関係がありません。実際、私はその人に同期ずれなしでリプレイを行う方法を教えました。
しかし、リプレイを作成する場合、標準を設定し、それに沿ってインフラストラクチャ全体を構築したいからです。楽しんでください、今度はあなたの番です。
見覚えがありますね?
まさに。これは、2013年にTH14のトライアル版のパッチを当てるためのthcrapの最初のビルドをリリースしたときに私がもたらしたのと同じ破壊的なエネルギーです。当時の古いガードはすぐに引退し、今度は私の番です。
こんにちは、ブルースカイです。私たちは決してバイブコーダーを応援しません! なぜ誰もそうするのでしょうか?
はい、知っています。あなたは政治的左派を代表しており、AIは基本的に資本主義勢力に対する2020年代の労働階級闘争であり、AIにすぐに影響を受けるアーティストと非常に親しい友人であり、彼らの苦しみを非常に敏感に感じています。また、AIバブルはもうすぐ破裂するでしょう。そして、これらのバイブコーダーの残りは、強力なクラウドベースのモデルがアクセスできなくなったり、法外に高価になったりしたら、何が残るのでしょうか? そうですよね?
現実は、残念ながら、はるかにニュアンスがあり、不快です。
-
ローカルLLMは、クラウド依存のあらゆる考えを打ち負かすようです。それらが実際にどれほど良いかはわかりませんが、それらを効率的に実行するための良いリグは、私の時間よりも安価です。そのようなリグでゲームをプレイすることもできます。最高のGPU設定で。お気に入りの巨大産業がポンプアウトした派手なトレーラーで宣伝されているように。
-
私たちが決してバイブコーダーを応援しないと言うなら、ほとんどの人は実際には非常に特定の、そしてはるかに条件付きの何かを意味します。もしAI使用の開示を見た場合、その時は癇癪を起こしてピッチフォークを取り出します。それ以前に懐疑的になり、経験のない人々からそのような印象的な結果がどのように得られるのか疑問に思うことは、 apparently、はるかに難しいです。特に、アーティストが私たちに見るように教えてくれた明確な視覚的な兆候がないソフトウェアについて話している場合。実際、ここにTwitterの人々が、通常のファンにこの種のハイプリテラシーを期待するのは完全に不合理だと主張しています。(アーカイブリンク) 人々をハイプさせることはできませんか、マン? 🙄 まあ、確かに、しかし、それはもはや働く価値のあるシーンではなくなり、私は出ていきます。🤷
-
バイブコーダーを、シーンでの事前の資格がないノーボディとして見ると、彼らを笑うのは簡単です。しかし、彼らが確立された尊敬されるコミュニティメンバーであり、コミュニティが経験している実際のペインポイントを解決していると主張する場合、それははるかに困難です。そこが応援の出所です。私がこれらのバイブコーダーが実際にはあなたの友人であり、実際に何をしているのかを知っている人々である可能性があることを明確にした後、表向きの反論で飛び込んだすべての人々は非常に静かになりました。結局のところ、それは単なるツールです、とある酔っ払いが昨年マイクにスラーしたように。
-
ゲームコミュニティがこれらの機能に対して圧倒的な需要を示したことを覚えておいてください。バイブコードされているかどうかに関係なく、彼らは気にしません。しかし、OK、あなたはまだイデオロギー的なレベルでバイブコードに反対しているかもしれません。そのため、AIの使用について友情を壊すほどです。そして、あなたは私の通常のケアレベルでReC98に資金を提供し続け、バイブコーダーが決して正当なアクターにはなれないと偽り続けるでしょう。
しかし、なぜ私が、本質的に重複したコードを書くためにあなたに予約され、私の役割をバイブコードに対する防波堤へとシフトさせる必要があるのでしょうか? そして、すでに断片化された東方コミュニティをさらに別の軸で断片化させるのでしょうか? -
なぜなら、そしてこれはあなたを驚かせるかもしれませんが、私は実際にはAIの使用のあらゆる種類に根本的に反対しているわけではありません。伝統的な検索エンジンの増加する衰退は、これが再び大金によって制御される技術移行であり、個々の抵抗はますます上り坂の戦いのように感じられることを証明しています。私はもっと嫌いなのは、オープンソースプロジェクトを、クレジットにアニータ・サルキーシアンがいるのを見た瞬間に却下する人々の増大する傾向です。つまり、リポジトリの
AGENTS.mdです。それは技術的な欠点を指摘する事実に基づいた議論ではなく、純粋にイデオロギー的なものです。怠惰であり、文脈やニュアンスを理解しようとさえしていません。
または、この種の行動。もし、31年前からメンテナンスをやめたランダムで時代遅れのコードに対するAIボットによる事前レビューを設定する権利を獲得した開発者がいるとしたら、それはKoizukaのようなベテランです。彼は、AIなしで、あらゆるレベルの抽象化で十分なコードを出荷しており、彼が熟練した開発者であることを証明しています。そして、驚くかもしれませんが、新しい時代のテクノロジーを着実に採用することが、彼が現在地に到達した唯一の方法です。そして確かに、彼はこれらの古い電子廃棄物をいじくり回すよりも、今日やるべきもっと良いことがあります。
Copilotがここにスロップした実際のコンテンツを分解することに焦点を当てることで、AIに対する議論はさらに強くなります。
- MASM/TLINKスタイルのツールチェーンについて話すことは、アセンブラとリンカを混同しています。これらは異なる種類のツールであり、このコードを非常に異なる方法で処理します。また、2つの異なるベンダーから来ています。
- TLINKはアセンブルされた.OBJファイルしか見ません。OMFフォーマットの
EXTDEFレコードには、ポインタ先のシンボルの型は格納されていません。したがって、リンク時にシンボル型の不一致エラーが発生する可能性はありません。 - オブジェクトファイルフォーマットとリンカの他の組み合わせがあり、エラーが発生する場合、それらを具体的に指定する必要があります。潜在的だが完全に定義されていないシナリオで何かが存在し、おそらく問題を引き起こす可能性があるということは、開発者が従うことができる最も麻痺させる思考の1つです。アレルギー反応を起こすべきことがあるとすれば、それはこの思考線です。これがBSD野郎をこれほど耐え難いものにしているのです。
- 8ビット値の配列を16ビットとして再宣言することは、最適化された使用コードが配列値を16ビットチャンクで自然にアクセスする場合、完全に有効です。これはASMであり、Cコードに適用される可能性のある一般的なコードの悪臭ルールは、この抽象化レベルには適用されません。
- しかし、それ以外にも:ボットは、コードがシンボルのアドレスを
segとoffsetとしてのみ使用し、命令がポインタ先のシンボルの背後にある値を逆参照しないことをすでに認識しています。したがって、最も厳密な正しい提案はEXTRN _SolidTileであり、ポインタ先の型は示されません。これはCのvoid *に相当します。 - しかし、それはこの提案は重要ではないことを意味します。最小限のdiffを好むなら、それを拒否しますが、適用しても害はありません。
老人はまだできるようです。
これは、AIに対して事実と論理で議論する方法です。そして、それはAIが地球に悪い、盗みであり、私たちをより愚かにし、私たちの仕事を奪うというあなたの既存の議論に、これらのすべての犯罪がこの特定のインスタンスで価値のあるものを何も生み出さなかったという事実を追加するだけです。
しかし、それには時間と労力がかかります。そして、人々はニュースをチェックするために他の30のDiscordサーバーを持っており、ポグ反応またはピッチフォークを広めています。そして、おそらく私はLiteの労力レベルに対して議論しているだけです。
いずれにせよ、これはコミュニティがすでにどれほど断片化しているかを示しており、悪化する一方だと恐れています。もし私がAIボットをセットアップして、私の時間を盗むべきではないPRから自分自身を守ったとしたらどうでしょうか? この技術が私に役立つかどうかを評価しようとしただけで、どれだけのコミュニティサポートを失うでしょうか? そして、おそらく最も重要なこと:この質問を、リリースサイクルを形成する何百もの質問に加えて、今、尋ねる必要があるのですか?! (プラス1、クソFreeBSDのため。)
- 最後に、それらにはテストスイートがあります。知っていますか、それは私が追加料金を請求する必要があるものです。なぜなら、私のすべての資金調達モデルは、機能を可能な限り高価にしないように調整されているからです。これらの6回のプッシュが準備していただけで、資金は私が2回失敗した後になって初めて受け取られました。そして確かに、今日ではテストスイートは何ですか?単にプロンプトするための別のものです。しかし、それらがテストスイートをプロンプトするという事実は、これらの人々がふざけておらず、標準を設定したいと考えていることを結論付けるのに十分です。
本質的に、これはバイブコーダーに関するものではありません。これは破壊的な攻撃であり、それに耐性がほとんどなかったビジネスモデルです。それは、3つの非常に具体的な柱に依存していたため、機能しました。
- 通常の東方パラドックス。過去18年間、同時に巨大で文化的に関連性があり、潜在的に大きな聴衆がいるにもかかわらず、キーインフラストラクチャを構築するために競合する開発者の大きな群衆がいないほど小さいことです。ご存知のとおり、過去16年間、私がこれらのアイデアを持っていて、実際に実装している唯一の人物はどのようにしてという感覚です。
- おそらく、このインフラストラクチャを構築する参入障壁がかつては実際に高かったためです。
- 私よりも優れた開発者がいたかもしれませんが、彼らは法的不確実性によって断念されたか、またはより注目度の高い、あるいは単に興味深いプロジェクトにすぐに移りました。ご存知のとおり、人々が1つのことに固執するのではなく、よりクールなものよりも、これらの優れた機会が自然に現れるような人生を送る場合に起こることです。
LLMとエージェントを導入して、仕事に十分強力になり、柱#2を倒し、新しい、若く、エネルギッシュな開発者を取り込み、ツールに関する先入観がなく、すべてを無視して新鮮に始められると信じているため、柱#3を揺さぶります。制約、そして物事は突然はるかに異なって見えます。次に、結果の競争が柱#1を粉砕し、私のビジネスモデル全体が崩壊します。
しかし、明らかに、彼らはあなたと協力したかったのではないでしょうか?
1か月間、何もに関与しようとさえせず、これらのゲームプレイの人々は今私にお金を払うことができるでしょうか? Seihouによって私のキャパシティがますます消費されるのを見て、それについて何もせずに4年間過ごした後? 自分自身を欺かないようにしましょう。考えられるあらゆる協力は、明らかに彼らの条件であり、私の条件ではありません。彼らの前で鞭打たれ、無給で、おそらく彼らのLLM出力をアンスロップすること全体から成ります。そして、私はまだその本当に屈辱的な仕事を示唆するオファーさえ受け取っていません。
7月10日の私の発表の後、何が起こったかはさらに物語っています。確かに、ツイートの後、4人の主要人物をブロックしました。それは、攻撃されていると感じたときに健康的なことだからです。しかし、両方の側面を知っていた他の誰も、私と解決策を模索しようとしなかったわけではありません。ゲームプレイの誰も、誤解を解消したり、何かを解決したりすることに興味がないように見えました。特に公には。老人は長い間迷惑になっていたようです。初心者の大衆には狂ったように見えさせ、陰で臆病に隠れる方が簡単です。
私がバイブコーダーの名前を公に挙げなかったことで、彼らにさえ有利なことをしていたようです。正直なところ、これは不要だと思っていました。もしあなたが本当に知りたいなら、GitHubのフォークネットワークを検索して簡単に見つけることができたはずです。さて、私が受け取ったすべてのものの量から判断すると、これは別の不合理な期待であることが判明しました。私は、私がタイムライン全体を説明した後、自分でそれを理解したtouhou-memories以外に、私のツイートを特定の、そして非常に公開されているフォークに結び付けた人を一人も見ませんでした。
おそらく、これらの関係者の誰かがより良くコミュニケーションをとっていれば、すべてが解決されたかもしれません。おそらく、これらのバイブコーダーはそれからボタンを押すのをやめたでしょう。しかし、ボタンを押さないすべての東方関連のバイブコーダーの気まぐれにのみ意味的に存在するビジネスを継続することは、狂った提案です。
ただ自分でバイブコードすればいいじゃないですか! 彼らをゲームで打ち負かすのです!
これは最も鈍感な提案に違いありません。過去数年間のブログ記事を読んだことがありますか、それともコードを見ましたか? 彼らは、経験を積み重ね、完璧にできる新しい角度が見え始めるにつれて、物事を行う「正しい方法」がますます複雑になるため、詳細に爆発しました。したがって、ますます小さな詳細について質問し、この深い理解を構築することが、このプロジェクトのポイント全体になり、そして長年このプロジェクトを続ける理由、東方という主題への興味を失ってからずっとです。これらの投稿を指して、はい、私はこれすべてを構築し、今ではここで書いたすべてを深く理解しており、私が稼いだお金は、このコンテンツが実際に求められていることを証明していますと言うのは素晴らしい気分です。バイブコーディングの場所はありますが、ReC98がその場所になることはありませんでした。
📝 マイクロトランザクションの概念は、より迅速な実際の進歩をもたらしたはずですが、市場はそれを完全に拒否し、4年間でわずかしか売れませんでした。代わりに、すべてのバックアップは、より遅く、より注意深い計画に完全に同意しました。もし私がやりたかったら、何年も前にゲームを盲目的に、そして迅速にデコンパイルできたでしょうが、誰も私にそれを価値のある行動にする説得力のあるオファーをしませんでした。東方ファンは一般的にゲームをプレイしませんが、彼らはゲームについて読むのが大好きで、これが彼らがますます支払ったものです。二次的なものや観光客について何を言っても、少なくとも彼らは実存的な脅威をもたらしません。
では、他の人がうまくバイブコードしているのに、なぜ今バイブコードする必要があるのでしょうか? 2018年のクラウドファンディングのDiscord時代以来、私の脳にはAIについて気にする余地はありませんでした。それは、私がまだ理解し、解決し、可能な限り妥協を少なく実装したいすべての実際の機能の計画でいっぱいでした。この愚かな軍拡競争は、私がこの分野で働く理由とはまったく関係のない、最も愚かで最も退廃的なもののように思えました。私の人生の選択の完全な誤りを私に見せてくれてありがとう、と思います。
しかし、あなたはまだこれらのブログ記事を書くことができたはずです! それらが大好きでした。
最終的にどれほど価値があったとしても、それらを書いてきたことは、常にこのプロジェクトの最も苦痛な側面でした。それほど苦痛だったので、最初に雇う従業員に執筆をアウトソースしたでしょう。機械がコードをより速く行うようになったので、コーディング作業を取り除くなら、ReC98の私が最も楽しんだものだけを取り除きます。前のブログ記事を書いた報酬、つまり。
皮肉なことに、これらの記事は元々、品質、ケア、そして将来の計画への多くの配慮を実証することによって、実際のコーディング作業を宣伝することを目的としていました。しかし今、このブログ全体は、バイブコーダーが結果を改善するためにLLMにフィードできるプロンプトコンテキストにすぎません。深いエンジンやハードウェアの知識は必要ありません。もしある怠惰な人がそれをすべて銀の皿に提供することをマーケティング戦略にしたなら。したがって、私はこの提案にサディスティックな悪意のヒントを検出せずにはいられません。私が多くの年の経験で満たされた人間が作った新鮮なコンテキストを提供するために、おそらくさらに少ないお金で、執拗に働き続けるべきでしょうか? そうすれば、バイブコーダーはそれらをすべてLLMに摂取し、コードに変換し、プレイ可能な結果のためにすべての名声と機会を得ることができます。素晴らしい仕事をしたという興奮が薄れると、私はもう何も得られません。
なぜなら、その場合でも、経済は成り立たないからです。
3つのポリシーバグ修正投稿(📝 1、📝 2、📝 3)、および📝 注文アドバイス投稿を除く。複雑さは、2020年以降の平均テキストバイト増加と平均アセットカウント増加の幾何平均として計算されます。つまり、√((平均テキストバイト / 4,234) × (平均アセットカウント / 0.9))。
| 年 | ブログ投稿数 | テキストバイト | 平均 | アセット数 | 平均 | 相対複雑度 |
|---|---|---|---|---|---|---|
| 2020 | 40 | 169,371 | 4,234 | 35 | 0.9 | 1.0 |
| 2021 | 23 | 146,709 | 6,379 | 22 | 1.0 | 1.3 |
| 2022 | 19 | 342,317 | 18,017 | 136 | 7.2 | 5.9 |
| 2023 | 13 | 389,428 | 29,956 | 117 | 9.0 | 8.5 |
| 2024 | 8 | 457,106 | 57,138 | 183 | 22.9 | 18.8 |
| 2025 | 11 | 603,909 | 54,901 | 191 | 17.4 | 16.0 |
私の資金調達基準から見ても、過去2年間のブログ投稿の複雑さの増加は、2020年のと比較して、*あたり少なくとも1回は正当化されたはずです。しかし、私がまで上がったとき、ストアは定期的に数か月間開いており、これはコミュニティが支払う意思があった額でした。ドイツの初級IT給与でさえ、ここではそれほど多くの自由を必要としないため、初級米国IT給与の半分にしかならないことを忘れてください。ましてや、人間的で非強制労働条件で従業員を雇用することなど。しかし、それさえも、私がこのプロジェクトで得たものよりもはるかに少ないです。
OK、落ち着いてください。問題のフォークを確認しましたが、確かにスロップです。あなたはこれを超えています。特に、時間をかけるのであればなおさらです。それでもこのプロジェクトに投資し続けます。以前よりもさらに。
さて、今、私たちはどこかに到達しています。なぜなら、これは以前のすべての議論を再フレーミングするからです。
- 私は、Discordで「ReC98をサポート」と「Nmlgcに10,000ドル」と唱えながら、実際にはそれを実行せず、計画を調整さえしなかったすべての人々を気にするべきではありませんでした。あるいは、クラウドファンディングシステムを回避し、損なう機会をすべて利用した人々。あるいは、私が引退を発表しなければ、決して隠れ家から出てこなかった静かな多数派。
実際、このプロジェクトは、もはや特定の東方サブコミュニティのコアによって運ばれていません。それらのコミュニティは騒がしく、目立っていたかもしれませんが、私のすべての注意を彼らに引きつけましたが、助けてはいませんでした。代わりに、最後の加入者はすべて、東方ファンダムのフリンジから来たか、またはすでに他のことに移っていました。彼らのサポートが、これをすべて実行し続けただけでなく、将来計画を立てることも可能にしました。そしていいえ、spaztron64、彼らは彼らが何を資金提供していたのかを一度も忘れていませんでした。なぜなら、彼らは後で皆私にそう言ったからです。 - 私は重複した作業を生成し、バイブコードに対する防波堤を形成することになりますが、それが私が実際にReC98であると知っている群衆から資金提供されている場合、それははるかに耐えられます。
- 資金が本当に確保されているなら、将来お金を払ってくれると期待して、すべての人に合わせる必要はもうありません。そして、しっかりとした境界を設定できます。ゲームプレイの人々との無料のやり取りはありません。レビューに時間がかかりすぎる悪いPRはすぐに閉じます。そのようなものです。
- 私はそれらの人々が実際に存在しているという感覚をより強く与えるために、コミュニティマネジメントをしてくれる人がいるかもしれませんが、どこで見つけるかはわかりません。有料で、もちろん。
しかし、今、私の人生の選択を再評価する十分な時間があったので、私は本当に少数の裕福なパトロンに再び頼りたいですか? もし私が宮崎駿のようなことをして、もう一つのことのために引退から戻ってきたら、今回は実際の群衆によって安全に運ばれることを好みます。
おそらく、それらのバイブコードがすべてスロップであることが判明した場合、その時点に到達できるかもしれません。基本的な品質要件さえ満たしていません。テストも役に立たず、開発者はまったく知らないので、それらを修正できません。現実には、彼らは無料でおもちゃで、ゲームへの愛のためにそれらを修正する、別の退屈で非常に有能な学生を見つけるでしょう。結局、thcrapも大丈夫だったと聞きました。(実際、最後に見た2024年には、私が去ったときよりもいくつかの場所でさらに醜く見えましたが、誰がそれを気にしますか。それは私のポイントを証明するだけです。)
結局のところ、十分によくやっていることが常に東方の本質でした。16年間この傾向と戦おうとした唯一の人がついに諦める時が来ました。
そして本当に、そのような環境で、この高レベルでこの仕事をする苦痛はまだ価値があるのでしょうか? AIのおかげで、すべてのゲームのコミュニティは、ボタンを押すだけでポートと完全なQoL機能を利用できるようになりました。これらのことを徹底的に実装することの価値の多くは、まさにそれらがまだ期待ではなく革新であるという事実にかかっています。しかし、その線を超えると、何も特別ではなくなり、誰もが1人の人が推論にお金を払い、ボタンを押し、そして結果を最小限に監督するのを待つだけでよくなります。
では、なぜ時間と人間的なタッチを投資する必要があるのでしょうか。革新の余地がないなら、別の場所で革新するように指示しているだけです。
3か月近く経ちました。新しいバイブコードされたフォークは現れていません。言及した4つのフォークのうち2つはどこにも行きませんでした。そのうちの1つは最初から回復不能なスロップでした。あなたは大幅に過剰反応していました。
当時、どうやって知ることができたでしょうか? この開発の欠如が、少なくとも間接的に私の引退ツイートから生じた可能性をどのように除外できますか? そして最も重要なこと:最も脅威となるフォークがまだ生きており、開発が続くにつれてその7月の逃走反応をますます正当化している場合、これら3つの他のフォークはどれほど重要ですか?
お願いです、続けてください! 上海アリス・リプライズとのつながりがあり、PC-98ゲームのリマスター版が出たら、きっとあなたを参加させます。あなたは明らかに、どんなバイブコーダーよりもその仕事に適しています。
誰もこれを言ったわけではありませんが(やった、NDA…)、この考えが有効に聞こえるという事実は、別の誤解を強調しています。
ReC98は、過去数年間でどれほど困難になったとしても、まさに私がやりたかったこと、そして取り組むことでした。thcrapの継続的な作業は、コミュニティが DRMで crippled された価値のない再リリースにお金を投げかけ続けたことを考えると、2019年初頭には道徳的に維持不可能になりました。2017年から2019年に忘れた場合、またはその場にいなかった場合、そしてそれが何度も繰り返されることができないため、単に:
Team Shanghai AliceによるSteam版のメインシリーズゲームを購入し、その上にthcrapを使用することを期待する場合、その組み合わせが機能するために文字通りのDRMクラックをthcrapが出荷することに依存しています。確かに、これらのクラックは作るのが難しくなく、この基本的なSteam DRMは、それが存在した痕跡を残さずに簡単に削除またはバイパスできます。しかし、それはそれをさらに弁護不可能にします。海賊は海賊行為をするでしょう、そして迅速な自動クラッキングステップは、彼らが侵害する著作権の量に質的な違いをもたらしません。しかし、モッダーにとっては、クラックを出荷する必要があり、以前は越えなかった別の種類の道徳的境界を越えるため、それは異なります。
Denuvoは、その圧倒的な強度とゲームへの深い統合が、理解可能な経済的目的を明確かつ間違いなく果たしているため、完全に弁護可能です。特にパブリッシャーが発売ウィンドウ後にそれを親切に削除した場合。しかし、Steam DRMとまったく同じCDリリースの上に重ねることはそうではありません。それは、MODを法的にさらに疑わしい領域に押し込む以外に、目的を果たしません。ZUNは、彼のゲームを翻訳パッチする人々への敬意と感謝のジェスチャーをしたかもしれませんが、彼は代わりに彼らの顔にDRMスイッチをスラップすることを選びました。彼はファン翻訳はクールだと言ったかもしれませんが、DRMスイッチをオンにするというこの1つの行動は、彼の以前のすべての言葉を無効にしました。
そして、誰かが暗号化はデフォルトであり、彼はただの老人という議論を持ってくる前に:彼は自分が何をしているか完全に知っていました。
そして、まだ隣接する橋を燃やしていないので、論理的な結論も表明します。
日本とのつながりがなく、言語を話せない西洋人として、Windows版東方にお金を払うことは道徳的に間違っており、むしろ海賊版をダウンロードすることは道徳的に正しいです。これらのゲームは私たち向けにマーケティングされていません。日本は、それらが何であるかに対して、これらのゲームを十分に資金提供しています。ZUNへのより多くの西洋のお金は、停滞を支持し、彼の前述の非難に値する行動を支持し、同時に、西洋コミュニティを築き上げたファン労働への権利意識を示します。文字通り、チェーンの他のすべてのリンクは、ZUNよりもあなたのお金に値します。Wiki著者、翻訳者、ジャーナリスト、あらゆる種類のウェブサイト運営者、アーティスト、さらには単なるソーシャルメディアのシットポスターでさえ、あなたはその名前を挙げてください。あなた方の大多数は、これらの人々の集団的な努力がなければ、ZUNをサポートしたくなかったでしょう。
thcrapの開発中に私が犯した最大の過ちは、コミュニティの平和という愚かな考えに屈し、Steam DRMクラックをthcrapに実装することでした。今日まで、私はそのプロジェクトの開発中に、私がフットダウンし、DRMクラックをthcrapに実装することを拒否し、問題を爆発させた代替タイムラインについて疑問に思い続けています。明らかに、結果の対立は私をどこかに連れて行ったでしょう。実際には何も得られなかったのとは対照的に。
2019年には、ReC98が私が個人的に支持できる唯一の想像可能なプロジェクトとして残った結論はこれでした。明らかに放棄されたこのメディア帝国の部分に焦点を当て、コミュニティのためにそれを開くことによって、私の仕事は現在マーケティングされている東方のいかなる側面からも利益を得ることはなく、ZUNをさらに豊かにすることもなく、少なくとも私が公正な分け前を得ることなしにはありません。したがって、このシーンで他のことをすることに興味はなく、特に、謙虚な同人コートを着たメディア帝国で、クローズドソースゲームに取り組むことに興味はありません。