# [Windows]Use Electron to deploy output RPG Maker MV/MZ games :::danger ![](https://i.imgur.com/XKmDnbq.png) ::: :::warning <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This article was translated using "[DeepL Translator](https://www.deepl.com/translator)". ::: This article will teach you to deploy your RPG Maker MV/MZ games using Electron. This tutorial will use Windows 10 (1909) operating system. :::danger <i class="fa fa-times-circle" aria-hidden="true"></i> Electron does **not** currently support **Windows Vista** and older versions of operating systems. ::: :::info **<i class="fa fa-info-circle" aria-hidden="true"></i> Update Info** **2022/05/03** Update part of the article. **2022/05/01** Fix some contents of package.json. **2022/01/04** Add block the feature of capturing screen. **2021/11/12** Added encrypted content about asar files. ::: 1.First, download Node.js for Windows OS from the following URL and install it on your computer. https://nodejs.org/en/download/ ![](https://i.imgur.com/2OEuO7c.png) 2.After the installation is complete, type `CMD` at the beginning to run the "Command Prompt", and type `node -v` and `npm -v` respectively to check if the installation is successful. ![](https://i.imgur.com/5Xc1hVn.png) ![](https://i.imgur.com/4NezzZ0.png) 3.After creating a folder, and add a new file named “package.json” and edit the file to add the following syntax content. :::warning <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> After adding the following syntax, remember to remove **//** and the comments that follow. ::: ```json= { "name": "RPG_Game", //The project name used for the output. //It must be alphanumeric and not contain any half-space. "version": "1.0.0", //Version number. //The format must be in the numeric format X.X.X. "description": "This is the Electron Deployment Test.", //File description. "main": "index.js", //[RPG Maker MZ] "chromium-args": "--force-color-profile=srgb", //[RPG Maker MV] "js-flags": "--expose-gc", "build": { "appId": "com.rpgmaker.game", //Application ID. //*You can usually enter com.xxxxx.yyyyy in alphanumeric format. //xxxxx is the English name of your game title. //yyyyy is the author's/team's name. "productName": "Game", //Game Title. "asar": true, //It is recommended to set this item to true if you need to seal the game content... "afterPack": "./myAfterPackHook.js", // * If you need to use the "asarmor" encryption suite, please make sure to include this parameter. "win": { "icon": "icon/icon.ico", //Game icon, the image format is ico format. //* Use the png to ico online conversion site and place the ico format image in the icon folder. "target": { "target": "dir" } } }, "scripts": { "start": "electron .", "pack": "electron-builder --dir", "dist": "electron-builder" }, "author": "Mirai", //The author's/team's name. "copyright": "Copyright © 2021 ${author} All rights reserved.", //Copyright Notice. "devDependencies": { "electron": "^11.5.0", "electron-builder": "^22.5.1", "asarmor": "^2.0.0" // * If you need to use the "asarmor" encryption suite... } } ``` :::info <i class="fa fa-info-circle" aria-hidden="true"></i> If you need to use the "asarmor" encryption suite, create a file called "myAfterPackHook.js" and put the following code into this file and save it in this output folder. ```javascript= const asarmor = require('asarmor'); const { join } = require("path"); exports.default = async ({ appOutDir, packager }) => { try { const asarPath = join(packager.getResourcesDir(appOutDir), 'app.asar'); console.log(`applying asarmor patches to ${asarPath}`); const archive = await asarmor.open(asarPath); archive.patch(); // apply default patches await archive.write(asarPath); } catch (err) { console.error(err); } }; ``` ::: **[Demo]** ![](https://i.imgur.com/F14E5fG.png) Then use the `cd` command on the "Command Prompt" to specify the path to the folder, and enter the `npm install` command on the "Command Prompt" to install the required package. :::warning <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Remember that folder names can only be alphanumeric and do not contain a half-line margin. If you need to use a half-line margin, please replace it with a ( _ ). ::: 4.Open the RPG Maker MV/MZ game project and click "File" → "Deployment...", select "Web Browsers / Android / iOS". When using RPG Maker MV, select "Web Browsers" and then click "OK" to wait for the deployment to complete. **[RPG Maker MZ]** ![](https://i.imgur.com/ds3WuKk.png) **[RPG Maker MV]** ![](https://i.imgur.com/FS6qsyO.png) 5.Create an "index.js" file ,and enter the following: ```javascript= const { app, BrowserWindow, ipcMain, shell } = require('electron'); function createWindow() { const win = new BrowserWindow({ width: 816, //Screen Width height: 624, //Screen Height icon: 'icon/icon.png', useContentSize: true, autoHideMenuBar: true, backgroundColor: '#000000', webPreferences: { nodeIntegration: true, contextIsolation: false } }) const isTest = false; //Whether it is a test mode. const testString = isTest ? '?test' : ''; app.allowRendererProcessReuse = false; win.loadURL(`file://${__dirname}/index.html${testString}`); if (process.platform !== 'darwin') { win.setMenu(null); } require('electron').ipcMain.on('focusMainWindow', function (e) { win.focus(); }); require('electron').ipcMain.on('openDevTools', function (e) { win.webContents.openDevTools(); }); require('electron').ipcMain.on('openExternal', function (e, arg) { shell.openExternal(arg); }); //* Required with NekoGakuen_BlockedCapture.js plugin. require('electron').ipcMain.on('setProtection', function (e, arg) { win.setContentProtection(arg); }); }; app.whenReady().then(createWindow); app.on('window-all-closed', () => { app.quit(); }); app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow(); } }); ``` 6.Go to the output folder ,and copy all the files except "package.json" and paste them into the output folder for this of Electron. **[RPG Maker MZ]** ![](https://i.imgur.com/OgrgQPr.png) **[RPG Maker MV]** ![](https://i.imgur.com/nFDZU8t.png) 7.Next, we will divide it into two projects and modify some of the contents in the js folder. **[RPG Maker MZ]** Modify "main.js", "rmmz_core.js" and "rmmz_managers.js" to the following. ## main.js ```javascript= isPathRandomized() { // [Note] We cannot save the game properly when Gatekeeper Path // Randomization is in effect. return ( Utils.isNwjs() && process.mainModule.filename.startsWith("/private/var") ); } ``` Replace.. ```javascript= isPathRandomized() { // [Note] We cannot save the game properly when Gatekeeper Path // Randomization is in effect. if (Utils.isElectronjs()) { return (__filename.startsWith("/private/var")); } else { return (Utils.isNwjs() && process.mainModule.filename.startsWith("/private/var")); } } ``` ## rmmz_core.js ```javascript= Utils.isNwjs = function() { return typeof require === "function" && typeof process === "object"; }; ``` Replace.. ```javascript= Utils.isNwjs = function() { return typeof require === "function" && typeof process === "object"; }; Utils.isElectronjs = function () { return window && window.process && window.process.versions && window.process.versions['electron']; }; ``` --- ```javascript= Utils.isOptionValid = function(name) { const args = location.search.slice(1); if (args.split("&").includes(name)) { return true; } if (this.isNwjs() && nw.App.argv.length > 0) { return nw.App.argv[0].split("&").includes(name); } return false; }; ``` Replace.. ```javascript= Utils.isOptionValid = function (name) { const args = location.search.slice(1); if (args.split("&").includes(name)) { return true; } if (this.isElectronjs()) { if (process.argv.length > 0) { return process.argv[0].split("&").includes(name); } } else { if (this.isNwjs() && nw.App.argv.length > 0) { return nw.App.argv[0].split("&").includes(name); } } return false; }; ``` ## rmmz_managers.js ```javascript= StorageManager.fileDirectoryPath = function() { const path = require("path"); const base = path.dirname(process.mainModule.filename); return path.join(base, "save/"); }; ``` Replace.. ```javascript= StorageManager.fileDirectoryPath = function () { const path = require("path"); if (Utils.isElectronjs()) { const base = path.dirname(__filename); return Utils.isOptionValid('test') ? path.join(base, "save/") : path.join(base, '../../save/'); } else { const base = path.dirname(process.mainModule.filename); return path.join(base, "save/"); } }; ``` --- ```javascript= SceneManager.reloadGame = function () { if (Utils.isNwjs()) { chrome.runtime.reload(); } }; SceneManager.showDevTools = function () { if (Utils.isNwjs() && Utils.isOptionValid("test")) { nw.Window.get().showDevTools(); } }; ``` Replace.. ```javascript= SceneManager.reloadGame = function () { if (Utils.isElectronjs()) { location.reload(); } else { if (Utils.isNwjs()) { chrome.runtime.reload(); } } }; SceneManager.showDevTools = function () { if (Utils.isElectronjs()) { if (Utils.isOptionValid("test")) { require('electron').ipcRenderer.send('openDevTools'); } } else { if (Utils.isNwjs() && Utils.isOptionValid("test")) { nw.Window.get().showDevTools(); } } }; ``` **[RPG Maker MV]** Modify “rpg_core.js” and “rpg_managers.js” to the following. ## rpg_core.js ```javascript= Utils.isNwjs = function() { return typeof require === 'function' && typeof process === 'object'; }; ``` Replace.. ```javascript= Utils.isNwjs = function() { return typeof require === 'function' && typeof process === 'object'; }; Utils.isElectronjs = function() { return window && window.process && window.process.versions && window.process.versions['electron']; }; ``` --- ```javascript= Utils.isOptionValid = function (name) { if (location.search.slice(1).split('&').contains(name)) { return 1; }; if (typeof nw !== "undefined" && nw.App.argv.length > 0 && nw.App.argv[0].split('&').contains(name)) { return 1; }; return 0; }; ``` Replace.. ```javascript= Utils.isOptionValid = function (name) { if (location.search.slice(1).split('&').contains(name)) { return 1; }; if (this.isElectronjs()) { if (this.isNwjs() && process.argv.length > 0 && process.argv[0].split('&').contains(name)) { return 1; }; } else { if (typeof nw !== "undefined" && nw.App.argv.length > 0 && nw.App.argv[0].split('&').contains(name)) { return 1; }; } return 0; }; ``` --- ```javascript= Input._wrapNwjsAlert = function() { if (Utils.isNwjs()) { var _alert = window.alert; window.alert = function() { var gui = require('nw.gui'); var win = gui.Window.get(); _alert.apply(this, arguments); win.focus(); Input.clear(); }; } }; ``` Replace.. ```javascript= Input._wrapNwjsAlert = function () { if (Utils.isElectronjs()) { var _alert = window.alert; window.alert = function () { _alert.apply(this, arguments); require('electron').ipcRenderer.send('focusMainWindow'); Input.clear(); }; } else { if (Utils.isNwjs()) { var _alert = window.alert; window.alert = function () { var gui = require('nw.gui'); var win = gui.Window.get(); _alert.apply(this, arguments); win.focus(); Input.clear(); }; } } }; ``` ## rpg_managers.js ```javascript= StorageManager.localFileDirectoryPath = function() { var path = require('path'); var base = path.dirname(process.mainModule.filename); return path.join(base, 'save/'); }; ``` Replace.. ```javascript= StorageManager.localFileDirectoryPath = function () { var path = require('path'); if (Utils.isElectronjs()) { var base = path.dirname(__filename); return Utils.isOptionValid('test') ? path.join(base, 'save/') : path.join(base, '../../save/'); } else { var base = path.dirname(process.mainModule.filename); return path.join(base, 'save/'); } }; ``` --- ```javascript= SceneManager.initialize = function() { this.initGraphics(); this.checkFileAccess(); this.initAudio(); this.initInput(); this.initNwjs(); this.checkPluginErrors(); this.setupErrorHandlers(); }; ``` Replace.. ```javascript= SceneManager.initialize = function () { this.initGraphics(); this.checkFileAccess(); this.initAudio(); this.initInput(); if (!Utils.isElectronjs()) { this.initNwjs(); } this.checkPluginErrors(); this.setupErrorHandlers(); }; ``` --- ```javascript= SceneManager.onKeyDown = function (event) { if (!event.ctrlKey && !event.altKey) { switch (event.keyCode) { case 116: // F5 if (Utils.isNwjs()) { location.reload(); } break; case 119: // F8 if (Utils.isNwjs() && Utils.isOptionValid('test')) { require('nw.gui').Window.get().showDevTools(); } break; } } }; ``` Replace.. ```javascript= SceneManager.onKeyDown = function (event) { if (!event.ctrlKey && !event.altKey) { switch (event.keyCode) { case 116: // F5 if (Utils.isElectronjs()) { location.reload(); } else { if (Utils.isNwjs()) { location.reload(); } } break; case 119: // F8 if (Utils.isElectronjs()) { if (Utils.isOptionValid('test')) { require('electron').ipcRenderer.send('openDevTools'); } } else { if (Utils.isNwjs() && Utils.isOptionValid('test')) { require('nw.gui').Window.get().showDevTools(); } } break; } } }; ``` :::info <i class="fa fa-info-circle" aria-hidden="true"></i> If the above is very complicated, you can also download quick start template directly from the following GitLab. https://gitlab.com/mmmm748748/rpg_maker_mv_and_mz_electron/-/tree/en_US/ ::: 8.Go back to "Command Prompt" and type `npm start` to check if the game can run normally. If the game runs OK, type `npm run dist` to run the deployment output. 9.Finally, after the deployment is finished, you can find the contents of the deployment in the "dist/win-unpacked/" folder and run Game.exe. ### <i class="fa fa-link" aria-hidden="true"></i> Reference Material https://www.electronjs.org https://github.com/electron/electron-quick-start https://qiita.com/RaTTiE/items/63f2e351a93f81bc8039 https://a091234765.pixnet.net/blog/post/402450719-%5Belectron%E5%AD%B8%E7%BF%92%E7%AD%86%E8%A8%98%5Delectron%E5%AE%89%E8%A3%9D%E6%AA%94%E6%89%93%E5%8C%85%E6%94%BB%E7%95%A5%E8%A3%9C%E5%85%85_el https://www.electron.build https://www.npmjs.com/package/asarmor Mirai - Patreon: https://www.patreon.com/MiraiDiary Mirai - Twitter(X): https://twitter.com/Mirai_so_Sad Mirai - itch.io: https://miraisosad.itch.io Mirai - Instagram https://www.instagram.com/miraisosad/ ###### tags: `貓咪學園` `NekoGakuen` `RPG Maker` `RPG Maker MV` `RPG Maker MZ`