備忘録

何かあったとき用に

PAYDAY2 Moddingから学ぶゲーム解析 #2

この記事はLava Bucket Advent Calendar 2017 10日目の記事です。
9日目の記事を先に見たことを前提として書いていきます。

Luaスクリプトを覗いてみる

Luaスクリプト群はcore/liblib/に存在しています。前者はこのゲームの基礎の基礎となる部分、つまりDiesel Engine*1の中心スクリプトとなる部分です。後者はこのゲーム自体のスクリプトとなる部分です。
さて、この中にLuaスクリプトが入ってるわけなのでそれを見ようと思いますが…。

f:id:KashEight:20171203192208p:plain (core/lib/utils/coreapp.luaの例)

あれ?
運の悪いことに.luaテキストエディタで開けません、どういうことでしょうか?
実はこのluaファイルはコンパイル済みであり、通常のテキストエディタで開くことはできません。なので、これらのLuaファイルをデコンパイルして読まなければなりません。
これを全てデコンパイルするのはかなり時間かかるのと手間がかかりますので有志の方が公開しているリポジトリをダウンロードかクローンして覗いてみましょう。
これらからお好きな方を選んでください。

  • bitbucket (アップデートの反映は早い、ただしgitでのクローンは不可)

  • github (gitでのクローンは可能、ただしアップデートの反映は遅い)

これでもう一回同じファイルを見てみましょう。

f:id:KashEight:20171203194248p:plain

これにて正常に開けました。

Luaスクリプトから推察する

さて、先程見れるようになったLuaスクリプトですが、これからpayday2のシステムを(一部ですが)推察してみましょう。

とりあえずデータを見てみる

今回はpayday2自体を見るのでlib/内のフォルダを見ることにしましょう。

f:id:KashEight:20171203195400p:plain
(lib/の内部)
この中には膨大というわけでもありませんが多くの量のスクリプトがあるので今回は"ゲームデータ"関連のスクリプトに焦点を当てて見てみましょう。
lib/内部をすべて解説するほどの労力がない(ごめんなさい)ので目的のフォルダだけを挙げます。今回の目的のフォルダはtweak_dataフォルダです。
f:id:KashEight:20171208231735p:plain
(lib/tweak_dataの内部)
内部だけでも大量のluaファイルが存在するのでさらに焦点を当ててみましょう。"キャラクター関連のゲームデータ"を探そうと思います。

f:id:KashEight:20171208232328p:plain
(lib/tweak_data/charactertweakdata.lua)の内部
まず、1行目から見てましょう。

CharacterTweakData = CharacterTweakData or class()

ここの定義からCharacterTweakDataはクラスとして定義されていることがわかります。*2
次に4行目を見てみましょう。

function CharacterTweakData:init(tweak_data)
…

initの詳しい説明は省きますがこの関数(メソッド)はオブジェクト指向型プログラミングでいうコンストラクタみたいなものです。ここでは、CharacterTweakDataクラスが呼び出されたときの初期化処理を行っています。
余談ですが、今回initの詳しい説明を省いたのはinit自体は別のファイル(クラス)で定義はされており、それを解説するとキリがないことや今回の目的とは少し遠くなるので説明を省きました。また今度違う記事で説明するかもしれません。

さて、このクラスについて基本部分はある程度の説明できました。あとは、各種のメソッドの処理がどうなっているか調べるだけです。
この部分が理解できればあなたも立派なHeisterです。いえい!(あいにく私は理解できていませんですが。)
もうほとんど書ききってしまったのとこれ以上書くと記事が長くなるのでここでお終いとします。
ここまで見てくださりありがとうございました。
明日はNagisberryさんの"ラズライト ~天藍石と青金石の関係性~"です。一体どういう記事なんでしょうか、楽しみです。

注釈

*1:OVERKILL Softwareの前身となるGRIN(現在は閉鎖済み)が開発したゲームエンジン

*2:補足: 今回は省略していますが、Luaは最初からクラスが提供されているわけではないため、クラスとして扱うには連想配列を使って定義する必要があります。1行目のclass()関数はPAYDAY 2のLuaスクリプト側で定義されたグローバル関数であり、Luaが直接持っている関数ではないので注意が必要です。class()関数の詳細は都合により省きます。

PAYDAY2 Moddingから学ぶゲーム解析 #1

この記事はLava Bucket Advent Calendar 2017 9日目の記事です。

まず最初に

PAYDAY 2とは?Moddingとは?

他のプレイヤーやNPCと最大4人のチームを組み、銀行強盗などのお仕事をこなすFPSです。
以下のキーワードに反応した方は是非チェックを。
 強盗
 CO-OP
 オブジェクティブベース
 エレクトロニック・ダンス・ミュージック
Payday2 日本語 WikiFAQ - どんなゲームより

そういうことですので、気になった方は購入を。

・・・

冗談は置いといて、少し真面目な解説をしましょう。

PAYDAY 2とは、 OVERKILL Softwareが制作、Starbreeze Studioがパブリッシャーの最大4人で遊ぶことができるCO-OP*1ゲームです。引用に書いてあるように内容は銀行強盗などのお仕事を受けてオブジェクティブ(目標)を達成しながらクリアを目指すゲームです。特に音楽に関しては評価がかなり高く、有名アーティストがPAYDAY 2用の曲を作成するほどの人気があります。ゲーム自体はC/C++*2で書かれており内部スクリプトLua*3で書かれています。そのため、内部スクリプトLuaをいじることでゲームの挙動を変えることができます。これがModdingです。これからこのModdingという行為からゲームの挙動の予測、つまり、解析を学ぼうと思います。

ゲームデータを展開する

まずゲーム解析を行うためにはゲームデータを展開させないと意味ありません。
PAYDAY 2はC:\Program Files (x86)\Steam\steamapps\common\PAYDAY 2\assetsにゲームデータを.bundle拡張子に変換して配置し、本体に読み込ませられるようにしています。
f:id:KashEight:20171124214722p:plain
(assetsの中身、およそ40GB以上ある。)

通常、.bundle拡張子の中身は見ることや展開することは不可能ですが、有志の方が作成したツールを使用することでそれらをすることができます。それで使うツールはこれです。

f:id:KashEight:20171124220015p:plain

使い方

前述してあるURLをクリックし、ダウンロードページへ移動、下画像の赤丸で囲んである"Download"をクリックしダウンロード、そして好きな場所へ解凍します。フォルダ作ってそこに格納したほうがいいかもしれません。

f:id:KashEight:20171124220550p:plain

解凍したファイルの中からPDBundleModPatcher.exeを実行、ダイアログが出ると思いますが迷わず[OK]と押しましょう。
そうすると紹介時の画像ような画面が映るかと思います。
そこからGame File Extractionタブをクリックしましょう。するとこういう画面に移ると思います。

f:id:KashEight:20171124221302p:plain

ここでのCustom Extract Folderは、展開先をどこにするかの指定です。中央左のBrowseをクリックして指定できます。ゲームデータは40GBを余裕で超えるので別ドライブに展開したら良いでしょう。
また、Extention Replacementは特定のワードを置換する機能です。PAYDAY 2はデータを読み込ませるために独特の拡張子を使用しています。一部のデータは拡張子を変えるだけで見れるのでこの機能を使い拡張子を一気に変更します。今回は.unitファイルを.xmlに、.textureファイルを.ddsにします。追加方法はExtentionに置換元のワード、Replacementに置換後のワードを入力してAdd/Updateをクリックするだけで追加できます。削除は削除したいものをクリックし、Removeを押せば削除できます。

これで、展開する準備が完了しました。

…と思いきや、まだやることがあります。

理由

まず、先程の状態から展開をスタートすると、「ちゃんと展開されるし問題ないのでは?」と思うかもしれませんが、実は一部のものしか展開できていません。
では一体どのファイルができていないのでしょうか?

ここで、この記事を見てみましょう。
この記事はall_<number>_h.bundleを作成するPython*4スクリプトといういうことがわかります。
ここからはBundle Modderの内部の話になりますが、Bundle Modderは.bundleをhashlist*5を元にhashを作成、それを参照し展開するような形をとっています。
そのため、もしhashが上手く作成されていない場合、.bundleを正常に展開することはできません。
ということは、このスクリプトはall_<number>_h.bundleを正常に読み込ませるためのスクリプトっていうことがわかります。
このため、展開する前にファイルを修正する必要があります。

.bundleファイルの修正, 及び更新

Python 2.7またはPython 3.x環境が必要です。

はじめに、hashlistのリポジトリダウンロードします。

Python 2.7を使用している場合

  1. hashlist, bundle fixer.pyを解凍する。
  2. hashlistをBundle Modderにあるフォルダに移動する。
  3. bundle fixer.pyを実行、all_h.bundleが作成されていることを確認する。
  4. Bundle Modderを起動、ファイルを展開する。

Python 3.xを使用している場合

  1. hashlistを解凍する。
  2. hashlistをBundle Modderにあるフォルダに移動する。
  3. ここからコードをペーストし、.py拡張子にして実行、all_h.bundleが作成されていることを確認する。
  4. Bundle Modderを起動、ファイルを展開する。

以上でファイルの展開が完了します、正常に展開されれば下画像のように展開されているはずです。

f:id:KashEight:20171125203535p:plain

アップデートで追加されたファイルを展開したい場合、先程のリポジトリを再度ダウンロード、hashlistをBundle Modderが存在するフォルダに移動させ、bundle fixer.pyを実行すればall_h.bundleが更新されます。
そして、Bundle Modderを起動して展開を実行すれば新しいファイルが追加されます。(この場合Ignore existing filesにチェックを入れることをオススメします。展開するファイルが既に存在している場合、展開しない処理をするので早く終わらせることができます。)

#1はここまでです。payday2の内部データの説明、解析は明日の#2で紹介いたします。
ここまで見てくださりありがとうございました。

注釈

Payday2のunitデータを解析する

経緯

日本語化MODをBLT2.x系に対応する際に「pager応答の字幕表示」という機能があり、それを対応、更新するのにLuaコードだけでは不十分で、他のところから参照する必要があった。Luaコード内には探しても探しても見つからないメソッドやクラス等が何個もあり、更新する際にそこがネックとなった。 また、従来の方法だとゲームを起動して探すという方法しかないためそれをどうにかしたいと思ったのもある。

元のコード

f:id:KashEight:20170919180719p:plain
上の画像はnbmd氏が作成した日本語MODの元のjp_p.luaのコード、ここでは

self._unit:sound_source(source):post_event(sound_name, callback_function, self._unit, "marker", "duration", "end_of_event")

上のsound_souce(), post_event()の2つのメソッドがLuaコード内で見つからなかった。

unitデータを探す

hook元となるlib\units\beings\player\playersound.luaからself._unitを探したところ序盤に

function PlayerSound:init(unit)
    self._unit = unit

    unit:base():post_init()

    local ss = unit:sound_source()

    ss:set_switch("robber", "rb3")

    if unit:base().is_local_player then
        ss:set_switch("int_ext", "first")
    else
        ss:set_switch("int_ext", "third")
    end
end

とコンストラクタがあったのでそれから探し出せないかと考えた。
とりあえずunitを定義しているものを探そうと思いpayday2\unitsをcmd開いて
for /r /d %i in (*) do ren %i\*.unit *.xml
を実行し、.unitファイルを全て.xmlファイルに置換した。(Bundle Modderにも置換機能あるんでそれを利用しても良い。)
それで実際に置換したものを開いてみる。
f:id:KashEight:20171118235309p:plain
(units\payday2\characters\civ_male_worker_1\civ_male_worker_1_husk.xmlの例)
見ても普通にわからないのでmodworkshopのwikiページを参考にしながら色々探ってみる。(詳しいことはwikiページにて、英語なので苦労するかもしれない。)
流石に全部説明するのは酷(自分も完全に理解してるわけではないのもある)なので説明は省略するが、重要と思えるところだけ取り上げてみる。

<extensions>
        <extension name="unit_data" class="ScriptUnitData" />
        <extension name="base" class="HuskCivilianBase" >
...

<extentions>タグで囲まれてる部分がunitデータの部分と属するクラスである。
おそらく、呼び出されたunitデータがどのクラスで処理しているかを示していると思われる。
ここから、目的のものを探すことはできるのではないかと思ったが事はうまく進まない。まあ、当たり前だろう。
目的のsound_sourceがどう処理されているかがわからないのである。そのため、pager応答がどう行われているかわからない。

振り出しに戻ってしまった。けっこうだるい。まあ、少しは進んだか。
一応、この記事を書いてる現在(17/11/19)はある程度進んだもののまだ先は長そうだ。
これ以上書くと長くなるので、続きは次回にしよう、userdata型とかIdstring型とかめんどいのもあるし。

今回の話とあんまり関係ないけれども

f:id:KashEight:20171119004111p:plain
どうやら音は「ケツ」から出ているらしい。面白い。

参考にしたサイト

modworkshop - Wiki - unitデータの詳しい解説が載っています。

Bundle Modderでpayday2の内部ファイルを展開した話

今現在payday2のModを作っているが、いかんせん情報も少なくluaソースはここ

bitbucket.org

で見てmoddingしなきゃいけないので今まではこれをcloneしてVSC*1で見ながらやってたりしてた。

しかし、どうも内部の.textureとかが参照できないので、内部ファイルを展開する必要があった。
それで、Bundle Modderというツールを使い40GBもあるpayday2ファイルを全て展開してみることにした。

f:id:KashEight:20170810203235p:plain
(実際の中身)

展開したpayday2の.textureファイル自体は開くことができないため、Bundle Modderに付属されている置換設定を利用し.textureを全て.ddsに置換し見れるものにする。
しかしながら、Windowsに搭載されている画像表示機能は巨大な容量の画像は表示されないのでGIMPなりPhotoshopなり使って開く必要がある。
勿論、自分が持っていたのはGIMPであり、Photoshop自体は持ってないしライセンス切れの製品も使う気がしなかったので、GIMPで開こうと思ったが、バニラGIMPは.ddsに対応してないため.ddsが見れるアドオンを入れることに。

アドオンを入れ、.ddsファイルを見てみたところpayday2に使われていない画像が入っており、おかしいと感じた。 いかにも、前作*2の画像が入っているのである。

f:id:KashEight:20170810205153p:plain
guis/textures/hud_icons.ddsの例

正直、payday2自体が40GBぐらいあるのは元からおかしいとは思っていたが、まさかpd:thのデータが入っていたとは… 実際、コードも見てて「あれ?」という部分もけっこうあったため今思えば納得いくけれども…
まさか、pd:thのコードを継ぎ足してpayday2を作ったというのは聞いたのだが本当だったとは思いもしなかった。

目的のものは見つかったので別にいいが、こういうのを放置してしまうと汚くなることがわかる一例を見た気がする…
自分もそういうのには気を付けようと思ったいい経験になった。

*1:Visual Studio Code

*2:PAYDAY: The Heist、pd:thと略されることが多い