Added support for drop-in mods on the UI.

This commit is contained in:
Daniel Scalzi
2018-08-07 04:16:15 -04:00
parent ff3f2bfb8d
commit 556199aa55
5 changed files with 296 additions and 18 deletions

View File

@@ -0,0 +1,109 @@
const fs = require('fs')
const path = require('path')
const { shell } = require('electron')
// Group #1: File Name (without .disabled, if any)
// Group #2: File Extension (jar, zip, or litemod)
// Group #3: If it is disabled (if string 'disabled' is present)
const MOD_REGEX = /^(.+(jar|zip|litemod))(?:\.(disabled))?$/
const DISABLED_EXT = '.disabled'
/**
* Scan for drop-in mods in both the mods folder and version
* safe mods folder.
*
* @param {string} modsDir The path to the mods directory.
* @param {string} version The minecraft version of the server configuration.
*
* @returns {{fullName: string, name: string, ext: string, disabled: boolean}[]}
* An array of objects storing metadata about each discovered mod.
*/
exports.scanForDropinMods = function(modsDir, version) {
const modsDiscovered = []
if(fs.existsSync(modsDir)){
let modCandidates = fs.readdirSync(modsDir)
let verCandidates = []
const versionDir = path.join(modsDir, version)
if(fs.existsSync(versionDir)){
verCandidates = fs.readdirSync(versionDir)
}
for(file of modCandidates){
const match = MOD_REGEX.exec(file)
if(match != null){
modsDiscovered.push({
fullName: match[0],
name: match[1],
ext: match[2],
disabled: match[3] != null
})
}
}
for(file of verCandidates){
const match = MOD_REGEX.exec(file)
if(match != null){
modsDiscovered.push({
fullName: path.join(version, match[0]),
name: match[1],
ext: match[2],
disabled: match[3] != null
})
}
}
}
return modsDiscovered
}
/**
* Delete a drop-in mod from the file system.
*
* @param {string} modsDir The path to the mods directory.
* @param {string} fullName The fullName of the discovered mod to delete.
*
* @returns {boolean} True if the mod was deleted, otherwise false.
*/
exports.deleteDropinMod = function(modsDir, fullName){
/*return new Promise((resolve, reject) => {
fs.unlink(path.join(modsDir, fullName), (err) => {
if(err){
reject(err)
} else {
resolve()
}
})
})*/
const res = shell.moveItemToTrash(path.join(modsDir, fullName))
if(!res){
shell.beep()
}
return res
}
/**
* Toggle a discovered mod on or off. This is achieved by either
* adding or disabling the .disabled extension to the local file.
*
* @param {string} modsDir The path to the mods directory.
* @param {string} fullName The fullName of the discovered mod to toggle.
* @param {boolean} enable Whether to toggle on or off the mod.
*
* @returns {Promise.<void>} A promise which resolves when the mod has
* been toggled. If an IO error occurs the promise will be rejected.
*/
exports.toggleDropinMod = function(modsDir, fullName, enable){
return new Promise((resolve, reject) => {
const oldPath = path.join(modsDir, fullName)
const newPath = path.join(modsDir, enable ? fullName.substring(0, fullName.indexOf(DISABLED_EXT)) : fullName + DISABLED_EXT)
fs.rename(oldPath, newPath, (err) => {
if(err){
reject(err)
} else {
resolve()
}
})
})
}
exports.isDropinModEnabled = function(fullName){
return !fullName.endsWith(DISABLED_EXT)
}

View File

@@ -4,6 +4,15 @@
/* Overlay Wrapper Functions */
/**
* Check to see if the overlay is visible.
*
* @returns {boolean} Whether or not the overlay is visible.
*/
function isOverlayVisible(){
return document.getElementById('main').hasAttribute('overlay');
}
/**
* Toggle the visibility of the overlay.
*

View File

@@ -3,6 +3,7 @@ const os = require('os')
const semver = require('semver')
const { AssetGuard } = require('./assets/js/assetguard')
const DropinModUtil = require('./assets/js/dropinmodutil')
const settingsState = {
invalid: new Set()
@@ -233,6 +234,7 @@ settingsNavDone.onclick = () => {
saveSettingsValues()
saveModConfiguration()
ConfigManager.save()
saveDropinModConfiguration()
switchView(getCurrentView(), VIEWS.landing)
}
@@ -450,7 +452,7 @@ function parseModulesForUI(mdls, submodules, servConf){
if(mdl.getRequired().isRequired()){
reqMods += `<div id="${mdl.getVersionlessID()}" class="settings${submodules ? 'Sub' : ''}Mod" enabled>
reqMods += `<div id="${mdl.getVersionlessID()}" class="settingsBaseMod settings${submodules ? 'Sub' : ''}Mod" enabled>
<div class="settingsModContent">
<div class="settingsModMainWrapper">
<div class="settingsModStatus"></div>
@@ -474,7 +476,7 @@ function parseModulesForUI(mdls, submodules, servConf){
const conf = servConf[mdl.getVersionlessID()]
const val = typeof conf === 'object' ? conf.value : conf
optMods += `<div id="${mdl.getVersionlessID()}" class="settings${submodules ? 'Sub' : ''}Mod" ${val ? 'enabled' : ''}>
optMods += `<div id="${mdl.getVersionlessID()}" class="settingsBaseMod settings${submodules ? 'Sub' : ''}Mod" ${val ? 'enabled' : ''}>
<div class="settingsModContent">
<div class="settingsModMainWrapper">
<div class="settingsModStatus"></div>
@@ -542,14 +544,16 @@ function saveModConfiguration(){
function _saveModConfiguration(modConf){
for(m of Object.entries(modConf)){
const tSwitch = settingsModsContainer.querySelectorAll(`[formod='${m[0]}']`)
if(typeof m[1] === 'boolean'){
modConf[m[0]] = tSwitch[0].checked
} else {
if(m[1] != null){
if(tSwitch.length > 0){
modConf[m[0]].value = tSwitch[0].checked
if(!tSwitch[0].hasAttribute('dropin')){
if(typeof m[1] === 'boolean'){
modConf[m[0]] = tSwitch[0].checked
} else {
if(m[1] != null){
if(tSwitch.length > 0){
modConf[m[0]].value = tSwitch[0].checked
}
modConf[m[0]].mods = _saveModConfiguration(modConf[m[0]].mods)
}
modConf[m[0]].mods = _saveModConfiguration(modConf[m[0]].mods)
}
}
}
@@ -557,7 +561,6 @@ function _saveModConfiguration(modConf){
}
function loadSelectedServerOnModsTab(){
const serv = DistroManager.getDistribution().getServer(ConfigManager.getSelectedServer())
document.getElementById('settingsSelServContent').innerHTML = `
@@ -583,13 +586,110 @@ function loadSelectedServerOnModsTab(){
`
}
document.getElementById("settingsSwitchServerButton").addEventListener('click', (e) => {
let CACHE_SETTINGS_MODS_DIR
let CACHE_DROPIN_MODS
function resolveDropinModsForUI(){
const serv = DistroManager.getDistribution().getServer(ConfigManager.getSelectedServer())
CACHE_SETTINGS_MODS_DIR = path.join(ConfigManager.getInstanceDirectory(), serv.getID(), 'mods')
CACHE_DROPIN_MODS = DropinModUtil.scanForDropinMods(CACHE_SETTINGS_MODS_DIR, serv.getMinecraftVersion())
let dropinMods = ''
for(dropin of CACHE_DROPIN_MODS){
dropinMods += `<div id="${dropin.fullName}" class="settingsBaseMod settingsDropinMod" ${!dropin.disabled ? 'enabled' : ''}>
<div class="settingsModContent">
<div class="settingsModMainWrapper">
<div class="settingsModStatus"></div>
<div class="settingsModDetails">
<span class="settingsModName">${dropin.name}</span>
<div class="settingsDropinRemoveWrapper">
<button class="settingsDropinRemoveButton" remmod="${dropin.fullName}">Remove</button>
</div>
</div>
</div>
<label class="toggleSwitch">
<input type="checkbox" formod="${dropin.fullName}" dropin ${!dropin.disabled ? 'checked' : ''}>
<span class="toggleSwitchSlider"></span>
</label>
</div>
</div>`
}
document.getElementById('settingsDropinModsContent').innerHTML = dropinMods
}
function bindDropinModsRemoveButton(){
const sEls = settingsModsContainer.querySelectorAll('[remmod]')
Array.from(sEls).map((v, index, arr) => {
v.onclick = () => {
const fullName = v.getAttribute('remmod')
const res = DropinModUtil.deleteDropinMod(CACHE_SETTINGS_MODS_DIR, fullName)
if(res){
document.getElementById(fullName).remove()
} else {
setOverlayContent(
`Failed to Delete<br>Drop-in Mod ${fullName}`,
'Make sure the file is not in use and try again.',
'Okay'
)
setOverlayHandler(null)
toggleOverlay(true)
}
}
})
}
function bindDropinModFileSystemButton(){
const fsBtn = document.getElementById('settingsDropinFileSystemButton')
fsBtn.onclick = () => {
shell.openItem(CACHE_SETTINGS_MODS_DIR)
}
}
function saveDropinModConfiguration(){
for(dropin of CACHE_DROPIN_MODS){
const dropinUI = document.getElementById(dropin.fullName)
if(dropinUI != null){
const dropinUIEnabled = dropinUI.hasAttribute('enabled')
if(DropinModUtil.isDropinModEnabled(dropin.fullName) != dropinUIEnabled){
DropinModUtil.toggleDropinMod(CACHE_SETTINGS_MODS_DIR, dropin.fullName, dropinUIEnabled).catch(err => {
if(!isOverlayVisible()){
setOverlayContent(
'Failed to Toggle<br>One or More Drop-in Mods',
err.message,
'Okay'
)
setOverlayHandler(null)
toggleOverlay(true)
}
})
}
}
}
}
document.getElementById('settingsSwitchServerButton').addEventListener('click', (e) => {
e.target.blur()
toggleServerSelection(true)
})
document.addEventListener('keydown', (e) => {
if(getCurrentView() === VIEWS.settings && selectedSettingsTab === 'settingsTabMods'){
if(e.key === 'F5'){
resolveDropinModsForUI()
bindDropinModsRemoveButton()
bindDropinModFileSystemButton()
bindModsToggleSwitch()
}
}
})
function animateModsTabRefresh(){
$('#settingsTabMods').fadeOut(500, () => {
saveModConfiguration()
ConfigManager.save()
saveDropinModConfiguration()
prepareModsTab()
$('#settingsTabMods').fadeIn(500)
})
@@ -600,6 +700,9 @@ function animateModsTabRefresh(){
*/
function prepareModsTab(first){
resolveModsForUI()
resolveDropinModsForUI()
bindDropinModsRemoveButton()
bindDropinModFileSystemButton()
bindModsToggleSwitch()
loadSelectedServerOnModsTab()
}