回想モードプラグインで、セーブデータ間のスイッチ情報を共有する機能の実装を検討していました。
その中で、セーブ・ロード時に利用するメソッドやゲームオブジェクトの変化といった詳細な情報が必要となり、調査したので記録に残します。
loadFromLocalFile(ローカル実行時のセーブファイル読み込み)
// rpg_managers.js 610行目 StorageManager.loadFromLocalFile = function(savefileId) { var data = null; var fs = require('fs'); var filePath = this.localFilePath(savefileId); if (fs.existsSync(filePath)) { data = fs.readFileSync(filePath, { encoding: 'utf8' }); } return LZString.decompressFromBase64(data); };
- this.localFilePathは、以下のような条件でパスを返す
- savefileIdが0より小さい場合:"config.rpgsave"を返す
- savefileIdが0と同じ場合:"global.rpgsave"を返す
- savefileIdが1以上の場合:"file%1.rpgsave".format(savefileId)を返す
loadFromWebStorage(ブラウザ実行時のセーブファイル読み込み)
// rpg_managers.js 639行目 StorageManager.loadFromWebStorage = function(savefileId) { var key = this.webStorageKey(savefileId); var data = localStorage.getItem(key); return LZString.decompressFromBase64(data); };
- this.webStorageKeyは以下のような条件でキー名を返す
- savefileIdが0より小さい場合:"RPG Config"
- savefileIdが0と同じ場合:"RPG Global"
- savefileIdが1以上の場合:"RPG File%1".format(savefileId);
saveToLocalFile(ローカル実行時のセーブファイル書き込み)
// rpg_managers.js 599行目 StorageManager.saveToLocalFile = function(savefileId, json) { var data = LZString.compressToBase64(json); var fs = require('fs'); var dirPath = this.localFileDirectoryPath(); var filePath = this.localFilePath(savefileId); if (!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath); } fs.writeFileSync(filePath, data); };
- 引数で受け取ったjsonをBase64にエンコード
saveToWebStorage(ブラウザ実行時のセーブファイル書き込み)
// rpg_managers.js 633行目 StorageManager.saveToWebStorage = function(savefileId, json) { var key = this.webStorageKey(savefileId); var data = LZString.compressToBase64(json); localStorage.setItem(key, data); };
ローカルか、ブラウザ実行かの判定
非常にわかりやすいメソッド名ですね。
// rpg_managers.js 595行目 StorageManager.isLocalMode = function() { return Utils.isNwjs(); };
セーブ(ローカル、ブラウザ)の判定
// rpg_managers.js 563行目 StorageManager.save = function(savefileId, json) { if (this.isLocalMode()) { this.saveToLocalFile(savefileId, json); } else { this.saveToWebStorage(savefileId, json); } };
セーブの呼び出し
json文字列の長さが200,000以上だった場合はワーニングを出力するようですね。ただ、その条件に一致する場合に変数操作は行っておらず、エラー等にはならないようです。
// rpg_managers.js 362行目 DataManager.saveGameWithoutRescue = function(savefileId) { var json = JsonEx.stringify(this.makeSaveContents()); if (json.length >= 200000) { console.warn('Save data too big!'); } StorageManager.save(savefileId, json); this._lastAccessedId = savefileId; var globalInfo = this.loadGlobalInfo() || []; globalInfo[savefileId] = this.makeSavefileInfo(); this.saveGlobalInfo(globalInfo); return true; };
- this.makeSaveContentsで、ゲーム変数($gameSystem等)をオブジェクトに格納
// rpg_managers.js 422行目 DataManager.makeSaveContents = function() { // A save data does not contain $gameTemp, $gameMessage, and $gameTroop. var contents = {}; contents.system = $gameSystem; contents.screen = $gameScreen; contents.timer = $gameTimer; contents.switches = $gameSwitches; contents.variables = $gameVariables; contents.selfSwitches = $gameSelfSwitches; contents.actors = $gameActors; contents.party = $gameParty; contents.map = $gameMap; contents.player = $gamePlayer; return contents; };
- ゲーム変数を格納したオブジェクトをjsonに変換し、563行目のsaveメソッドに渡す
- makeSavefileInfoメソッドで、セーブ画面に表示するキャラクターやプレイ時間等の情報をオブジェクトにセットする
// game_managers.js 411行目 DataManager.makeSavefileInfo = function() { var info = {}; info.globalId = this._globalId; info.title = $dataSystem.gameTitle; info.characters = $gameParty.charactersForSavefile(); info.faces = $gameParty.facesForSavefile(); info.playtime = $gameSystem.playtimeText(); info.timestamp = Date.now(); return info; };
ロード(ローカル、ブラウザ)の判定
StorageManager.load = function(savefileId) { if (this.isLocalMode()) { return this.loadFromLocalFile(savefileId); } else { return this.loadFromWebStorage(savefileId); } };
ロードの呼び出し
DataManager.loadGameWithoutRescue = function(savefileId) { var globalInfo = this.loadGlobalInfo(); if (this.isThisGameFile(savefileId)) { var json = StorageManager.load(savefileId); this.createGameObjects(); this.extractSaveContents(JsonEx.parse(json)); this._lastAccessedId = savefileId; return true; } else { return false; } };
- StorageManager.loadで、セーブファイルから文字列(Base64からデコード済み)を取得
- this.createGameObjectsで、Game_XXXXオブジェクトを生成
// game_managers.js 194行目 DataManager.createGameObjects = function() { $gameTemp = new Game_Temp(); $gameSystem = new Game_System(); $gameScreen = new Game_Screen(); $gameTimer = new Game_Timer(); $gameMessage = new Game_Message(); $gameSwitches = new Game_Switches(); $gameVariables = new Game_Variables(); $gameSelfSwitches = new Game_SelfSwitches(); $gameActors = new Game_Actors(); $gameParty = new Game_Party(); $gameTroop = new Game_Troop(); $gameMap = new Game_Map(); $gamePlayer = new Game_Player(); };
- セーブファイルの文字列を、jsonパースし、ゲーム変数($gameSystem等)に格納
// game_managers.js 438行目 DataManager.extractSaveContents = function(contents) { $gameSystem = contents.system; $gameScreen = contents.screen; $gameTimer = contents.timer; $gameSwitches = contents.switches; $gameVariables = contents.variables; $gameSelfSwitches = contents.selfSwitches; $gameActors = contents.actors; $gameParty = contents.party; $gameMap = contents.map; $gamePlayer = contents.player; };
それでは、皆様も楽しいツクールライフを!