You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

525 lines
18 KiB
JavaScript

//=============================================================================
// Salted Fish Plugins - Auto Update
// SF_AutoUpdate.js
//=============================================================================
"use strict";
var Imported = Imported || {};
Imported.SF_AutoUpdate = true;
var SF_Plugins = SF_Plugins || {};
//=============================================================================
/*:
* @plugindesc v1.0.0 - Automatically update the game.
* @author Salted Fish
*
* @help
*
* ===========================================================================
* Introduction
* ===========================================================================
*
* This plugin automatically updates the game.
*
* ===========================================================================
* Requires
* ===========================================================================
*
* You need use ycrpg2-android-client to run on android.
* otherwise, you can use ycrpg2-pc-client to run on pc.
* This is not supported on ios and web, but web also use the newest version.
*
* ===========================================================================
* How to use
* ===========================================================================
*
* ycrpg2-android-client provide FileUtils and UpdateUtils.
* FileUtils is used to read and write Text files, and UpdateUtils is used to
* update the game.
*
* FileUtils provide the following functions:
* public void evaluateJavascript(String script)
* public boolean canExecute(String fileName)
* public boolean canRead(String fileName)
* public boolean canWrite(String fileName)
* public boolean createNewFile(String fileName)
* public boolean delete(String fileName)
* public boolean exists(String fileName)
* public String getAbsolutePath(String fileName)
* public String getCanonicalPath(String fileName)
* public String getName(String fileName)
* public String getParent(String fileName)
* public String getPath(String fileName)
* public boolean isAbsolute(String fileName)
* public boolean isDirectory(String fileName)
* public boolean isFile(String fileName)
* public long lastModified(String fileName)
* public long length(String fileName)
* public String list(String fileName)
* public boolean mkdir(String fileName)
* public boolean mkdirs(String fileName)
* public boolean renameTo(String srcName, String dstName)
* public String toString(String fileName)
* public String readTextFile(String fileName)
* public boolean writeTextFile(String fileName,String content)
* public String getFileHashHex(String fileName, String algorithm)
* public String getFileHashHex(String fileName)
*
* UpdateUtils provide the following functions:
* public void evaluateJavascript(String script)
* public void downloadFullUrl(String fileName, String urlString, String success, String fail)
* public void downloadRelativeUrl(String fileName, String urlString, String success, String fail)
* public void startUpdateFiles()
* public void updateFileCompleted(boolean bool)
* public String getHashInfoJson()
* public void updateFile(String fileName, String success, String fail)
*/
//=============================================================================
(function () {
var SF_AutoUpdate = {};
SF_Plugins.SF_AutoUpdate = SF_AutoUpdate;
SF_AutoUpdate.version = 1.0;
SF_AutoUpdate.remoteFileInfoName = "file_info_remote.json";
SF_AutoUpdate.localFileInfoName = "file_info_local.json";
SF_AutoUpdate.remoteFileInfoUrl = "https://ycrpg.xyzzgame.com/ycrpg2/file_info_remote.json";
SF_AutoUpdate.remoteUrlRoot = "https://ycrpg.xyzzgame.com/YCrpg2/";
SF_AutoUpdate.emptyFileInfoStr = `{"is_file":false,"is_dir":true,"children":[],"sha_512":"","file_name":""}`;
SF_AutoUpdate.workerFileName = "js/plugins/SF_AutoUpdateWorker.js";
SF_AutoUpdate.localStorageKey = "SF_AutoUpdate_UpdateFileCompleted";
SF_AutoUpdate.enableAutoUpdate = true;
SF_AutoUpdate.isAndroid = function () {
return !!window.FileUtils && !!window.UpdateUtils && !SF_AutoUpdate.isPC();
};
SF_AutoUpdate.isPC = function () {
return !!Utils.isNwjs();
};
SF_AutoUpdate.isWeb = function () {
return !SF_AutoUpdate.isPC() && !SF_AutoUpdate.isAndroid();
};
SF_AutoUpdate.isSupported = function () {
return (
SF_AutoUpdate.enableAutoUpdate &&
(SF_AutoUpdate.isAndroid() || SF_AutoUpdate.isPC()) &&
!Utils.isOptionValid("test")
);
};
if (!SF_AutoUpdate.isSupported()) {
return;
}
SF_AutoUpdate.FileUtils = window.FileUtils || {};
SF_AutoUpdate.UpdateUtils = window.UpdateUtils || {};
if (SF_AutoUpdate.isPC()) {
window.FileUtils = SF_AutoUpdate.FileUtils;
window.UpdateUtils = SF_AutoUpdate.UpdateUtils;
var fs = require("fs");
var path = require("path");
var os = require("os");
var crypto = require("crypto");
var https = require("https");
var httpsAgent = new https.Agent({
keepAlive: true,
});
var options = {
agent: httpsAgent,
};
SF_AutoUpdate.FileUtils.evaluateJavascript = function (script) {
new Function(script).call(window);
};
SF_AutoUpdate.FileUtils.canExecute = function (fileName) {
try {
fs.accessSync(fileName, fs.X_OK);
return true;
} catch (e) {
return false;
}
};
SF_AutoUpdate.FileUtils.canRead = function (fileName) {
try {
fs.accessSync(fileName, fs.R_OK);
return true;
} catch (e) {
return false;
}
};
SF_AutoUpdate.FileUtils.canWrite = function (fileName) {
try {
fs.accessSync(fileName, fs.W_OK);
return true;
} catch (e) {
return false;
}
};
SF_AutoUpdate.FileUtils.createNewFile = function (fileName) {
try {
fs.writeFileSync(fileName, "");
return true;
} catch (e) {
return false;
}
};
SF_AutoUpdate.FileUtils.delete = function (fileName) {
try {
fs.unlinkSync(fileName);
return true;
} catch (e) {
return false;
}
};
SF_AutoUpdate.FileUtils.exists = function (fileName) {
try {
fs.accessSync(fileName, fs.F_OK);
return true;
} catch (e) {
return false;
}
};
SF_AutoUpdate.FileUtils.getAbsolutePath = function (fileName) {
return path.resolve(fileName);
};
SF_AutoUpdate.FileUtils.getCanonicalPath = function (fileName) {
return path.resolve(fileName);
};
SF_AutoUpdate.FileUtils.getName = function (fileName) {
return path.basename(fileName);
};
SF_AutoUpdate.FileUtils.getParent = function (fileName) {
return path.dirname(fileName);
};
SF_AutoUpdate.FileUtils.getPath = function (fileName) {
return path.resolve(fileName);
};
SF_AutoUpdate.FileUtils.isAbsolute = function (fileName) {
return path.isAbsolute(fileName);
};
SF_AutoUpdate.FileUtils.isDirectory = function (fileName) {
return fs.statSync(fileName).isDirectory();
};
SF_AutoUpdate.FileUtils.isFile = function (fileName) {
return fs.statSync(fileName).isFile();
};
SF_AutoUpdate.FileUtils.lastModified = function (fileName) {
return fs.statSync(fileName).mtime.getTime();
};
SF_AutoUpdate.FileUtils.length = function (fileName) {
return fs.statSync(fileName).size;
};
SF_AutoUpdate.FileUtils.mkdir = function (fileName) {
try {
fs.mkdirSync(fileName);
return true;
} catch (e) {
return false;
}
};
SF_AutoUpdate.FileUtils.mkdirs = function (fileName) {
try {
fs.mkdirSync(fileName, { recursive: true });
return true;
} catch (e) {
return false;
}
};
SF_AutoUpdate.FileUtils.reanmeTo = function (srcName, dstName) {
try {
fs.renameSync(oldFileName, newFileName);
return true;
} catch (e) {
return false;
}
};
SF_AutoUpdate.FileUtils.toString = function (fileName) {
return path.resolve(fileName);
};
SF_AutoUpdate.FileUtils.readTextFile = function (fileName) {
return fs.readFileSync(fileName, "utf-8");
};
SF_AutoUpdate.FileUtils.writeTextFile = function (fileName, text) {
return fs.writeFileSync(fileName, text, "utf-8");
};
SF_AutoUpdate.FileUtils.getFileHashHex = function (fileName, algorithm) {
var hash = crypto.createHash(algorithm || "SHA512");
var data = fs.readFileSync(fileName);
hash.update(data);
return hash.digest("hex");
};
SF_AutoUpdate.UpdateUtils.evaluateJavascript = function (script) {
new Function(script).call(window);
};
SF_AutoUpdate.UpdateUtils.downloadFullUrl = function (fileName, url, success, fail) {
var file = fs.createWriteStream(fileName);
https
.get(url, options, function (response) {
response.pipe(file);
file.on("finish", function () {
file.close(
SF_AutoUpdate.UpdateUtils.evaluateJavascript.bind(SF_AutoUpdate.UpdateUtils, success)
);
});
})
.on("error", function (err) {
SF_AutoUpdate.UpdateUtils.evaluateJavascript.bind(SF_AutoUpdate.UpdateUtils, fail)(err.message);
});
};
SF_AutoUpdate.UpdateUtils.downloadRelativeUrl = function (fileName, url, success, fail) {
SF_AutoUpdate.UpdateUtils.downloadFullUrl(fileName, SF_AutoUpdate.remoteUrlRoot + url, success, fail);
};
SF_AutoUpdate.UpdateUtils.updateFileCompleted = function (isSuccess, needReload) {
localStorage.setItem(SF_AutoUpdate.localStorageKey, JsonEx.stringify(isSuccess));
if (isSuccess && needReload) {
nw.Window.open(`chrome-extension://${chrome.runtime.id}/index.html`, { new_instance: true });
nw.Window.get().close();
}
};
SF_AutoUpdate.UpdateUtils.getHashInfoJson = function () {
try {
return fs.readFileSync(SF_AutoUpdate.localFileInfoName) || SF_AutoUpdate.emptyFileInfoStr;
} catch (e) {
return SF_AutoUpdate.emptyFileInfoStr;
}
};
SF_AutoUpdate.UpdateUtils.updateFile = function (fileName, success, fail) {
SF_AutoUpdate.UpdateUtils.downloadRelativeUrl(fileName, fileName, success, fail);
};
SF_AutoUpdate.UpdateUtils.startUpdateFiles = function () {
localStorage.setItem(SF_AutoUpdate.localStorageKey, "false");
};
}
//=============================================================================
// Scene_AutoUpdate
//=============================================================================
function Scene_AutoUpdate() {
this.initialize.apply(this, arguments);
}
window.Scene_AutoUpdate = Scene_AutoUpdate;
SF_AutoUpdate.Scene_AutoUpdate = Scene_AutoUpdate;
Scene_AutoUpdate.prototype = Object.create(Scene_Base.prototype);
Scene_AutoUpdate.prototype.constructor = Scene_AutoUpdate;
Scene_AutoUpdate.prototype.initialize = function () {
Scene_Base.prototype.initialize.call(this);
this._localFileInfo = JsonEx.parse(UpdateUtils.getHashInfoJson());
this._remoteFileInfo = {};
this._updateFileList = [];
this._updateFileIndex = 0;
this._updateFileCount = 0;
this._updateFile = {};
this._updateSuccess = false;
this._deleteFileList = [];
this._status = "completed"; // "working", "completed"
this._job = ""; // "fetch remote file info", "compare file info", "delete file", "update file"
this._nextJob = "fetched remote file info";
this._compareWorker = new Worker(SF_AutoUpdate.workerFileName);
this._compareWorker.onmessage = this._onCompareWorkerMessage.bind(this);
};
Scene_AutoUpdate.prototype.create = function () {
Scene_Base.prototype.create.call(this);
};
Scene_AutoUpdate.prototype.start = function () {
Scene_Base.prototype.start.call(this);
};
Scene_AutoUpdate.prototype.update = function () {
Scene_Base.prototype.update.call(this);
this.updateJob();
this.updateLoading();
};
Scene_AutoUpdate.prototype.updateLoading = function () {
console.log(this._job, this._status);
};
Scene_AutoUpdate.prototype.updateJob = function () {
if (this._status === "working") {
return;
}
if (this._nextJob === "") {
CallBack.clear();
SceneManager.pop();
}
this._job = this._nextJob;
this._status = "working";
switch (this._job) {
case "fetched remote file info":
this.fetchRemoteFileInfo();
this._nextJob = "compare file info";
break;
case "compare file info":
this.compareFileInfo();
this._nextJob = "delete file";
break;
case "delete file":
this.deleteFile();
this._nextJob = "update file";
break;
case "update file":
this.updateFile();
this._nextJob = "update completed";
break;
case "update completed":
this._status = "completed";
this._nextJob = "";
FileUtils.writeTextFile(SF_AutoUpdate.localFileInfoName, JsonEx.stringify(this._remoteFileInfo));
UpdateUtils.updateFileCompleted(this._updateSuccess, this._updateFileCount > 0);
break;
}
};
Scene_AutoUpdate.prototype.fetchRemoteFileInfo = function () {
var success = function () {
this._remoteFileInfo = JsonEx.parse(FileUtils.readTextFile(SF_AutoUpdate.remoteFileInfoName));
this._status = "completed";
}.bind(this);
var fail = function () {
this._status = "completed";
this._nextJob = "";
}.bind(this);
var callBacks = CallBack.registerOneTimeList([success, fail]);
UpdateUtils.downloadRelativeUrl(
SF_AutoUpdate.remoteFileInfoName,
SF_AutoUpdate.remoteFileInfoName,
callBacks[0],
callBacks[1]
);
};
Scene_AutoUpdate.prototype.compareFileInfo = function () {
this._compareWorker.postMessage({
command: "compare",
local_file_info: this._localFileInfo,
remote_file_info: this._remoteFileInfo,
});
};
Scene_AutoUpdate.prototype._onCompareWorkerMessage = function (e) {
var result = e.data;
if (result.command === "delete") {
this._deleteFileList = this._deleteFileList.concat(result.file_list);
} else if (result.command === "update") {
this._updateFileList = this._updateFileList.concat(result.file_list);
} else {
this._status = "completed";
}
};
Scene_AutoUpdate.prototype.deleteFile = function () {
UpdateUtils.startUpdateFiles();
this._deleteFileList.forEach(function (file_info) {
FileUtils.delete(file_info.file_name);
});
this._status = "completed";
};
Scene_AutoUpdate.prototype.updateFile = function () {
this._updateFileIndex = 0;
this._updateFileCount = this._updateFileList.length;
if (this._updateFileCount === 0) {
this._status = "completed";
return;
}
this._updateFile = this._updateFileList[this._updateFileIndex];
this.updateFileNext();
};
Scene_AutoUpdate.prototype.updateFileNext = function () {
var success = function () {
this._updateFileIndex++;
if (this._updateFileIndex >= this._updateFileCount) {
this._status = "completed";
this._updateSuccess = true;
} else {
this._updateFile = this._updateFileList[this._updateFileIndex];
this.updateFileNext();
}
}.bind(this);
var fail = function () {
// auto try again
this.updateFileNext();
}.bind(this);
var file = this._updateFile;
if (file.is_dir) {
success();
return;
}
if (!FileUtils.exists(FileUtils.getParent(file.file_name))) {
FileUtils.mkdirs(FileUtils.getParent(file.file_name));
}
var callBacks = CallBack.registerOneTimeList([success, fail]);
UpdateUtils.downloadRelativeUrl(file.file_name, file.file_name, callBacks[0], callBacks[1]);
};
//=============================================================================
// SceneManager
//=============================================================================
SF_AutoUpdate.SceneManager_initialize = SceneManager.initialize;
SceneManager.initialize = function () {
SF_AutoUpdate.SceneManager_initialize.call(this);
this.addSceneBefore(Scene_AutoUpdate, Scene_Title);
};
})();