Source: StorageManager.js

StorageManager.js

//-----------------------------------------------------------------------------
// StorageManager
//
// The static class that manages storage for saving game data.
/**
 * The static class that manages storage for saving game data.
 *
 * @namespace
 */
function StorageManager() {
    throw new Error("This is a static class");
}

StorageManager._forageKeys = [];
StorageManager._forageKeysUpdated = false;

/**
 * Check if the game is running locally (Nwjs)
 *
 * @static
 * @return {boolean} If game is running locally (mostly means not mobile/web)
 */
StorageManager.isLocalMode = function() {
    return Utils.isNwjs();
};

/**
 * Save an object
 *
 * @static
 * @param {string} saveName - Name of the save file
 * @param {Object} object - The object to save
 */
StorageManager.saveObject = function(saveName, object) {
    return this.objectToJson(object)
        .then(json => this.jsonToZip(json))
        .then(zip => this.saveZip(saveName, zip));
};

/**
 * Load a saved object
 *
 * @static
 * @param {string} saveName - Name of the save file to load
 */
StorageManager.loadObject = function(saveName) {
    return this.loadZip(saveName)
        .then(zip => this.zipToJson(zip))
        .then(json => this.jsonToObject(json));
};

/**
 * Converts given object to JSON via stringify
 *
 * @static
 * @param {Object} object - Object to convert to JSON string
 * @return {Promise} Promise object of the converted JSON
 */
StorageManager.objectToJson = function(object) {
    return new Promise((resolve, reject) => {
        try {
            const json = JsonEx.stringify(object);
            resolve(json);
        } catch (e) {
            reject(e);
        }
    });
};

/**
 * Converts given JSON to object via parsing
 *
 * @static
 * @param {string} json - JSON string to convert to object
 * @return {Promise} Promise object of the converted object
 */
StorageManager.jsonToObject = function(json) {
    return new Promise((resolve, reject) => {
        try {
            const object = JsonEx.parse(json);
            resolve(object);
        } catch (e) {
            reject(e);
        }
    });
};

/**
 * Converts given JSON to zip
 *
 * @static
 * @param {string} json - JSON string to convert
 * @return {Promise} Promise object of the converted zip
 */
StorageManager.jsonToZip = function(json) {
    return new Promise((resolve, reject) => {
        try {
            const zip = pako.deflate(json, { to: "string", level: 1 });
            if (zip.length >= 50000) {
                console.warn("Save data is too big.");
            }
            resolve(zip);
        } catch (e) {
            reject(e);
        }
    });
};

/**
 * Converts given zip to JSON
 *
 * @static
 * @param {string} zip - ZIP string to convert
 * @return {Promise} Promise object of the converted JSON
 */
StorageManager.zipToJson = function(zip) {
    return new Promise((resolve, reject) => {
        try {
            if (zip) {
                const json = pako.inflate(zip, { to: "string" });
                resolve(json);
            } else {
                resolve("null");
            }
        } catch (e) {
            reject(e);
        }
    });
};

/**
 * Saves a zip formatted string
 *
 * @static
 * @param {string} saveName - Name of the save file
 * @param {string} zip - ZIP string to save
 * @return {Promise} Promise object for saving data
 */
StorageManager.saveZip = function(saveName, zip) {
    if (this.isLocalMode()) {
        return this.saveToLocalFile(saveName, zip);
    } else {
        return this.saveToForage(saveName, zip);
    }
};

/**
 * Loads a zip formatted string
 *
 * @static
 * @param {string} saveName - Name of the save file
 * @return {Promise} Promise object for loading data
 */
StorageManager.loadZip = function(saveName) {
    if (this.isLocalMode()) {
        return this.loadFromLocalFile(saveName);
    } else {
        return this.loadFromForage(saveName);
    }
};

/**
 * Checks if a save file of the given name exists
 *
 * @static
 * @param {string} saveName - Name of the save file
 * @return {boolean} True if file exists, otherwise false
 */
StorageManager.exists = function(saveName) {
    if (this.isLocalMode()) {
        return this.localFileExists(saveName);
    } else {
        return this.forageExists(saveName);
    }
};

/**
 * Removes a given save file
 *
 * @static
 * @param {string} saveName - Name of the save file
 * @return {Promise} Promise object for removing a save file
 */
StorageManager.remove = function(saveName) {
    if (this.isLocalMode()) {
        return this.removeLocalFile(saveName);
    } else {
        return this.removeForage(saveName);
    }
};

/**
 * Saves a file to the local environment
 *
 * @static
 * @param {string} saveName - Name of the save file
 * @param {string} zip - Zip string to save
 * @return {Promise} Promise object for saving the file
 */
StorageManager.saveToLocalFile = function(saveName, zip) {
    const dirPath = this.fileDirectoryPath();
    const filePath = this.filePath(saveName);
    const backupFilePath = filePath + "_";
    return new Promise((resolve, reject) => {
        this.fsMkdir(dirPath);
        this.fsUnlink(backupFilePath);
        this.fsRename(filePath, backupFilePath);
        try {
            this.fsWriteFile(filePath, zip);
            this.fsUnlink(backupFilePath);
            resolve();
        } catch (e) {
            try {
                this.fsUnlink(filePath);
                this.fsRename(backupFilePath, filePath);
            } catch (e2) {
                //
            }
            reject(e);
        }
    });
};

/**
 * Loads a file from the local environment
 *
 * @static
 * @param {string} saveName - Name of the save file
 * @return {Promise} Promise object for the data in the file
 */
StorageManager.loadFromLocalFile = function(saveName) {
    const filePath = this.filePath(saveName);
    return new Promise((resolve, reject) => {
        const data = this.fsReadFile(filePath);
        if (data) {
            resolve(data);
        } else {
            reject(new Error("Savefile not found"));
        }
    });
};

/**
 * Checks if a save file of the given name exists locally
 *
 * @static
 * @param {string} saveName - Name of the save file
 * @return {boolean} True if file exists, otherwise false
 */
StorageManager.localFileExists = function(saveName) {
    const fs = require("fs");
    return fs.existsSync(this.filePath(saveName));
};

/**
 * Removes a local save file
 *
 * @static
 * @param {string} saveName - Name of the save file
 */
StorageManager.removeLocalFile = function(saveName) {
    this.fsUnlink(this.filePath(saveName));
};

/**
 * Saves data to browser storage
 *
 * @static
 * @param {string} saveName - Name of the save key
 * @param {string} zip - Zip string to save
 * @return {Promise} Promise object for saving
 */
StorageManager.saveToForage = function(saveName, zip) {
    const key = this.forageKey(saveName);
    const testKey = this.forageTestKey();
    setTimeout(() => localforage.removeItem(testKey));
    return localforage
        .setItem(testKey, zip)
        .then(() => localforage.setItem(key, zip))
        .then(() => this.updateForageKeys());
};

/**
 * Loads data from browser storage
 *
 * @static
 * @param {string} saveName - Name of the save key
 * @return {Promise} Promise object for loading data
 */
StorageManager.loadFromForage = function(saveName) {
    const key = this.forageKey(saveName);
    return localforage.getItem(key);
};

/**
 * Checks if a save key exists in browser storage
 *
 * @static
 * @param {string} saveName - Name of the save key
 * @return {boolean} True if key exists, else false
 */
StorageManager.forageExists = function(saveName) {
    const key = this.forageKey(saveName);
    return this._forageKeys.includes(key);
};

/**
 * Removes save data from browser storage
 *
 * @static
 * @param {string} saveName - Name of the save key
 */
StorageManager.removeForage = function(saveName) {
    const key = this.forageKey(saveName);
    return localforage.removeItem(key).then(() => this.updateForageKeys());
};

/**
 * Update localforage save keys for browser storage
 *
 * @static
 */
StorageManager.updateForageKeys = function() {
    this._forageKeysUpdated = false;
    return localforage.keys().then(keys => {
        this._forageKeys = keys;
        this._forageKeysUpdated = true;
        return 0;
    });
};

/**
 * Checks if localforage keys have been updated
 *
 * @static
 * @return {boolean} True if keys are updated
 */
StorageManager.forageKeysUpdated = function() {
    return this._forageKeysUpdated;
};

/**
 * Makes a directory (synchronously)
 *
 * @static
 * @param {string} path - The path to make the directory
 */
StorageManager.fsMkdir = function(path) {
    const fs = require("fs");
    if (!fs.existsSync(path)) {
        fs.mkdirSync(path);
    }
};

/**
 * Renames a file (synchronously)
 *
 * @static
 * @param {string} oldPath - The old path where the file existed
 * @param {string} newPath - The new path to change the file to
 */
StorageManager.fsRename = function(oldPath, newPath) {
    const fs = require("fs");
    if (fs.existsSync(oldPath)) {
        fs.renameSync(oldPath, newPath);
    }
};

/**
 * Deletes a file (synchronously)
 *
 * @static
 * @param {string} path - The path to the file
 */
StorageManager.fsUnlink = function(path) {
    const fs = require("fs");
    if (fs.existsSync(path)) {
        fs.unlinkSync(path);
    }
};

/**
 * Reads a file (synchronously)
 *
 * @static
 * @param {string} path - The path to the file
 * @return {string} The contents of the file
 */
StorageManager.fsReadFile = function(path) {
    const fs = require("fs");
    if (fs.existsSync(path)) {
        return fs.readFileSync(path, { encoding: "utf8" });
    } else {
        return null;
    }
};

/**
 * Writes a file (synchronously)
 *
 * @static
 * @param {string} path - The path to the file
 * @param {string} data - The data to write
 */
StorageManager.fsWriteFile = function(path, data) {
    const fs = require("fs");
    fs.writeFileSync(path, data);
};

/**
 * Gets the path to the save file directory
 *
 * @static
 * @return {string} The path to the save file directory
 */
StorageManager.fileDirectoryPath = function() {
    const path = require("path");
    const base = path.dirname(process.mainModule.filename);
    return path.join(base, "save/");
};

/**
 * Gets the path to the save file of the given name
 *
 * @static
 * @param {string} saveName - The name of the save file
 * @return {string} The path to the save file
 */
StorageManager.filePath = function(saveName) {
    const dir = this.fileDirectoryPath();
    return dir + saveName + ".rmmzsave";
};

/**
 * Gets the path to the save data of the given name
 *
 * @static
 * @param {string} saveName - The name of the save key
 * @return {string} The forage key for the given save name
 */
StorageManager.forageKey = function(saveName) {
    const gameId = $dataSystem.advanced.gameId;
    return "rmmzsave." + gameId + "." + saveName;
};

/**
 * A test key for localforage
 *
 * @static
 * @return {string} The forage key for the test data
 */
StorageManager.forageTestKey = function() {
    return "rmmzsave.test";
};