Closed Spoonail-Iroiro closed 2 months ago
modSave
の取得の仕方ですが、maginai.modSave
で取得するより以下のように引数で明示的に渡すのが良いと思います。
maginai.events.saveLoading.addHandler((modSave) => {
modSave.getSaveObject("mod");
};
maginai.events.saveObjectRequired.addHandler((modSave) => {
modSave.setSaveObject("mod", save);
};
maginai
からの取得だと個々のセーブデータとは別に保存できるように感じるかもしれません。
個人的にはこんなインターフェースが好みです。
maginai.modSave.load(modName, (modData) => {
});
maginai.modSave.save(modName, () => {
return modData;
});
いっそmodSave
にはこのくらいのヘルパー関数addSaveHandlers
があってもいいかもしれない
let myModValue = 0;
const createSaveObject = () => ({myModValue});
const loadSaveObject = (modSaveObject) => myModValue = modData.myModValue;
maginai.modSave.addSaveHandlers(
'myMod',
createSaveObject,
loadSaveObject
);
このaddSaveHandlers
の呼び出しは下記のコードと同じ
maginai.events.saveLoading.addHandler(() => {
const modSaveObject = maginai.modSave.getSaveObject('myMod');
loadSaveObject(modSaveObject);
};
maginai.events.saveObjectRequired.addHandler(() => {
const modSaveObject = createSaveObject();
maginai.modSave.setSaveObject('myMod', modSaveObject);
};
下段のコードの通り、イベントsaveLoading
/saveObjectRequired
の方はイベント引数なしでいきたい
理由:
個人的にはイベントハンドラでmodSave
サブモジュールを引数で受け取れてもあまりわかりやすくならないように見えるため。
enzaki氏の2番目の例のように直接Mod用セーブオブジェクトを受け取れればわかりやすいが、そのためにはmaginai側はaddHandler
時点でMod名を受け取る必要がありaddHandler
のインターフェースにそぐわない。
補足:
modSave.addSaveHandlers
はほぼenzaki氏のsave
/load
を1つのメソッドにまとめたメソッドだが、変更点と変更理由は以下の通り。
■2つあるメソッドを1つに統合
結局このmodSave.addSaveHandlers
がhigh-level APIでmodSave.getSaveObject
/modSave.setSaveObject
/events.saveLoading
/events.saveObjectRequired
がlow-level APIという関係になるので
modSave.addSaveHandlers
は便利な、ワンストップなメソッドとしていいのではないか、という理由。
■名前をハンドラー登録ということをわかりやすく
save
/load
だとどうしてもすぐセーブ・ロードを実行するように見えるのと、個別のセーブデータがセーブ/ロードされるたびにハンドラーが呼び出されるように見えにくいため。
わたしの出した最初の例ですが、modSave
をsaveLoading
とsaveObjectRequired
の外で使う理由がないことをAPIレベルで表現したほうがよいのでは、という意図でした。(javascriptなのでルールに違反することは簡単ですが)言葉足らずですみません。
maginai.modSave.load(modName, (modData) => { }); maginai.modSave.save(modName, () => { return modData; });
こっちの例は他のModのセーブデータにアクセスしてはいけない、というのをAPIで表現したものです。
maginai.events.saveLoading.addHandler(() => { const modSaveObject = maginai.modSave.getSaveObject('myMod'); loadSaveObject(modSaveObject); }; maginai.events.saveObjectRequired.addHandler(() => { const modSaveObject = createSaveObject(); maginai.modSave.setSaveObject('myMod', modSaveObject); };
これだとドキュメントを読み込まないと正しいAPIの使い方ができないのでは?と思います。
events.saveLoading
はmodSave.getSaveObject
以外を目的に使われて、modSave.getSaveObject
はevents.saveLoading
以外で使われることになります。
たとえlow-level APIであれど意図しない使い方を可能にする理由はありません。
low-level APIで簡単に他のModのセーブデータにアクセスできるのならmodSave.addSaveHandlers
はなくても良いでしょう。
save
/load
だとどうしてもすぐセーブ・ロードを実行するように見えるのと、個別のセーブデータがセーブ/ロードされるたびにハンドラーが呼び出されるように見えにくいため。
save
/load
の命名に関してはその通りだと思います。
他のModのセーブデータにアクセス
これについてはあるModをさらに拡張するMod等でありうるユースケースだとは思ってる
これらの、Mod用セーブデータの操作インターフェースについて 最終的にはどういう形がいいと思ってます?
maginai.modSave.addLoadHandler(modName, (modData) => {
});
maginai.modSave.addSaveHandler(modName, () => {
return modData;
});
このようなsave
/load
をハンドラだと分かる命名にしたものがいいと思ってますが、既存のハンドラのインターフェースにそぐわないと言われたらそうですね。
Modのセーブデータにいつでもどこからでも触れるようにするのならmaginai.events.saveLoading.addHandler
/maginai.modSave.getSaveObject
のほうがいいと思います。
いっそ
modSave
にはこのくらいのヘルパー関数addSaveHandlers
があってもいいかもしれないlet myModValue = 0; const createSaveObject = () => ({myModValue}); const loadSaveObject = (modSaveObject) => myModValue = modData.myModValue; maginai.modSave.addSaveHandlers( 'myMod', createSaveObject, loadSaveObject );
この
addSaveHandlers
の呼び出しは下記のコードと同じmaginai.events.saveLoading.addHandler(() => { const modSaveObject = maginai.modSave.getSaveObject('myMod'); loadSaveObject(modSaveObject); }; maginai.events.saveObjectRequired.addHandler(() => { const modSaveObject = createSaveObject(); maginai.modSave.setSaveObject('myMod', modSaveObject); };
これで実装したい
ただしaddSaveHandlers
は少し変更
addSaveHandler
→addSaveObjectHandler
にsaveLoaded
と同様)let myModValue = 0;
const createSaveObject = () => ({myModValue});
const loadSaveObject = (modSaveObject, isNewGame) => {
if (isNewGame) {
myModValue=1;
}
else {
myModValue = modSaveObject?.myModValue ?? 2;
}
};
maginai.modSave.addSaveObjectHandlers(
'myMod',
createSaveObject,
loadSaveObject
);
一応確認です。実装するのはこの三つですか?
maginai.events.saveLoading
maginai.events.saveObjectRequired
maginai.modSave.addSaveObjectHandlers
maginai.events.saveLoading
maginai.modSave.addSaveObjectHandlers
この2つです。
maginai.events.saveObjectRequired
はすでに存在していますね。
今更ながらaddSaveObjectHandlers
で「ロード時にname
に対応するセーブオブジェクトが存在しない」ときのhandlerは別にしたほうがいいのではとなった
let myModValue = 0;
const initializeWithoutSaveObject = (isNewGame) => {
myModValue = isNewGame ? 1 : 2;
}
const loadSaveObject = (modSaveObject) => {
myModValue = modSaveObject.myModValue;
};
const createSaveObject = () => ({myModValue});
maginai.modSave.addSaveObjectHandlers(
'myMod',
initializeWithoutSaveObject,
loadSaveObject,
createSaveObject,
);
これは以下と同じ
maginai.events.saveLoading.addHandler((e) => {
const modSaveObject = maginai.modSave.getSaveObject('myMod');
if (modSaveObject === undefined) {
initializeWithoutSaveObject(e.isNewGame);
}
else {
loadSaveObject(modSaveObject);
}
};
maginai.events.saveObjectRequired.addHandler(() => {
const modSaveObject = createSaveObject();
maginai.modSave.setSaveObject('myMod', modSaveObject);
};
参考:https://github.com/limoka/DSP-Mods/blob/master/Mods/DSPModSave/src/DSPModSave/IModCanSave.cs
セーブデータが選択されて、各ゲームオブジェクトへのデータのセットが始まる前のイベント
saveLoading
を追加する。saveLoading
イベントではMod用セーブオブジェクトへのアクセスができる。理由:
maginai.modSave
でのMod用セーブオブジェクトの取得をsaveLoaded
イベントで行うと、イベントより前の処理をセーブデータに保存された値に基づいて行えないため。 たとえばtGameRoutineMap.viewMap
はsaveLoaded
イベントより前に呼ばれるため、tGameRoutineMap.viewMap
にメソッドパッチした場合、そのメソッドはsaveLoaded
イベントが起こるより前に呼ばれてしまう。saveLoading
イベントとsaveLoaded
イベントの使い分け・原則は以下の通りmaginai.modSave
でのMod用セーブオブジェクトの取得はsaveLoading
イベントで行うtGameCharactor.charas
に格納されたキャラ情報等)がロードされセットされた直後に行いたい処理はsaveLoaded
イベント (従来通り)考慮事項
saveLoading
名称が適切かどうかmodSaveLoaded
?maginai.modSave
の使用例の更新