dropinmodutil.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. const fs = require('fs-extra')
  2. const path = require('path')
  3. const { ipcRenderer, shell } = require('electron')
  4. const { SHELL_OPCODE } = require('./ipcconstants')
  5. // Group #1: File Name (without .disabled, if any)
  6. // Group #2: File Extension (jar, zip, or litemod)
  7. // Group #3: If it is disabled (if string 'disabled' is present)
  8. const MOD_REGEX = /^(.+(jar|zip|litemod))(?:\.(disabled))?$/
  9. const DISABLED_EXT = '.disabled'
  10. const SHADER_REGEX = /^(.+)\.zip$/
  11. const SHADER_OPTION = /shaderPack=(.+)/
  12. const SHADER_DIR = 'shaderpacks'
  13. const SHADER_CONFIG = 'optionsshaders.txt'
  14. /**
  15. * Validate that the given directory exists. If not, it is
  16. * created.
  17. *
  18. * @param {string} modsDir The path to the mods directory.
  19. */
  20. exports.validateDir = function(dir) {
  21. fs.ensureDirSync(dir)
  22. }
  23. /**
  24. * Scan for drop-in mods in both the mods folder and version
  25. * safe mods folder.
  26. *
  27. * @param {string} modsDir The path to the mods directory.
  28. * @param {string} version The minecraft version of the server configuration.
  29. *
  30. * @returns {{fullName: string, name: string, ext: string, disabled: boolean}[]}
  31. * An array of objects storing metadata about each discovered mod.
  32. */
  33. exports.scanForDropinMods = function(modsDir, version) {
  34. const modsDiscovered = []
  35. if(fs.existsSync(modsDir)){
  36. let modCandidates = fs.readdirSync(modsDir)
  37. let verCandidates = []
  38. const versionDir = path.join(modsDir, version)
  39. if(fs.existsSync(versionDir)){
  40. verCandidates = fs.readdirSync(versionDir)
  41. }
  42. for(let file of modCandidates){
  43. const match = MOD_REGEX.exec(file)
  44. if(match != null){
  45. modsDiscovered.push({
  46. fullName: match[0],
  47. name: match[1],
  48. ext: match[2],
  49. disabled: match[3] != null
  50. })
  51. }
  52. }
  53. for(let file of verCandidates){
  54. const match = MOD_REGEX.exec(file)
  55. if(match != null){
  56. modsDiscovered.push({
  57. fullName: path.join(version, match[0]),
  58. name: match[1],
  59. ext: match[2],
  60. disabled: match[3] != null
  61. })
  62. }
  63. }
  64. }
  65. return modsDiscovered
  66. }
  67. /**
  68. * Add dropin mods.
  69. *
  70. * @param {FileList} files The files to add.
  71. * @param {string} modsDir The path to the mods directory.
  72. */
  73. exports.addDropinMods = function(files, modsdir) {
  74. exports.validateDir(modsdir)
  75. for(let f of files) {
  76. if(MOD_REGEX.exec(f.name) != null) {
  77. fs.moveSync(f.path, path.join(modsdir, f.name))
  78. }
  79. }
  80. }
  81. /**
  82. * Delete a drop-in mod from the file system.
  83. *
  84. * @param {string} modsDir The path to the mods directory.
  85. * @param {string} fullName The fullName of the discovered mod to delete.
  86. *
  87. * @returns {Promise.<boolean>} True if the mod was deleted, otherwise false.
  88. */
  89. exports.deleteDropinMod = async function(modsDir, fullName){
  90. const res = await ipcRenderer.invoke(SHELL_OPCODE.TRASH_ITEM, path.join(modsDir, fullName))
  91. if(!res.result) {
  92. shell.beep()
  93. console.error('Error deleting drop-in mod.', res.error)
  94. return false
  95. }
  96. return true
  97. }
  98. /**
  99. * Toggle a discovered mod on or off. This is achieved by either
  100. * adding or disabling the .disabled extension to the local file.
  101. *
  102. * @param {string} modsDir The path to the mods directory.
  103. * @param {string} fullName The fullName of the discovered mod to toggle.
  104. * @param {boolean} enable Whether to toggle on or off the mod.
  105. *
  106. * @returns {Promise.<void>} A promise which resolves when the mod has
  107. * been toggled. If an IO error occurs the promise will be rejected.
  108. */
  109. exports.toggleDropinMod = function(modsDir, fullName, enable){
  110. return new Promise((resolve, reject) => {
  111. const oldPath = path.join(modsDir, fullName)
  112. const newPath = path.join(modsDir, enable ? fullName.substring(0, fullName.indexOf(DISABLED_EXT)) : fullName + DISABLED_EXT)
  113. fs.rename(oldPath, newPath, (err) => {
  114. if(err){
  115. reject(err)
  116. } else {
  117. resolve()
  118. }
  119. })
  120. })
  121. }
  122. /**
  123. * Check if a drop-in mod is enabled.
  124. *
  125. * @param {string} fullName The fullName of the discovered mod to toggle.
  126. * @returns {boolean} True if the mod is enabled, otherwise false.
  127. */
  128. exports.isDropinModEnabled = function(fullName){
  129. return !fullName.endsWith(DISABLED_EXT)
  130. }
  131. /**
  132. * Scan for shaderpacks inside the shaderpacks folder.
  133. *
  134. * @param {string} instanceDir The path to the server instance directory.
  135. *
  136. * @returns {{fullName: string, name: string}[]}
  137. * An array of objects storing metadata about each discovered shaderpack.
  138. */
  139. exports.scanForShaderpacks = function(instanceDir){
  140. const shaderDir = path.join(instanceDir, SHADER_DIR)
  141. const packsDiscovered = [{
  142. fullName: 'OFF',
  143. name: 'Off (Default)'
  144. }]
  145. if(fs.existsSync(shaderDir)){
  146. let modCandidates = fs.readdirSync(shaderDir)
  147. for(let file of modCandidates){
  148. const match = SHADER_REGEX.exec(file)
  149. if(match != null){
  150. packsDiscovered.push({
  151. fullName: match[0],
  152. name: match[1]
  153. })
  154. }
  155. }
  156. }
  157. return packsDiscovered
  158. }
  159. /**
  160. * Read the optionsshaders.txt file to locate the current
  161. * enabled pack. If the file does not exist, OFF is returned.
  162. *
  163. * @param {string} instanceDir The path to the server instance directory.
  164. *
  165. * @returns {string} The file name of the enabled shaderpack.
  166. */
  167. exports.getEnabledShaderpack = function(instanceDir){
  168. exports.validateDir(instanceDir)
  169. const optionsShaders = path.join(instanceDir, SHADER_CONFIG)
  170. if(fs.existsSync(optionsShaders)){
  171. const buf = fs.readFileSync(optionsShaders, {encoding: 'utf-8'})
  172. const match = SHADER_OPTION.exec(buf)
  173. if(match != null){
  174. return match[1]
  175. } else {
  176. console.warn('WARNING: Shaderpack regex failed.')
  177. }
  178. }
  179. return 'OFF'
  180. }
  181. /**
  182. * Set the enabled shaderpack.
  183. *
  184. * @param {string} instanceDir The path to the server instance directory.
  185. * @param {string} pack the file name of the shaderpack.
  186. */
  187. exports.setEnabledShaderpack = function(instanceDir, pack){
  188. exports.validateDir(instanceDir)
  189. const optionsShaders = path.join(instanceDir, SHADER_CONFIG)
  190. let buf
  191. if(fs.existsSync(optionsShaders)){
  192. buf = fs.readFileSync(optionsShaders, {encoding: 'utf-8'})
  193. buf = buf.replace(SHADER_OPTION, `shaderPack=${pack}`)
  194. } else {
  195. buf = `shaderPack=${pack}`
  196. }
  197. fs.writeFileSync(optionsShaders, buf, {encoding: 'utf-8'})
  198. }
  199. /**
  200. * Add shaderpacks.
  201. *
  202. * @param {FileList} files The files to add.
  203. * @param {string} instanceDir The path to the server instance directory.
  204. */
  205. exports.addShaderpacks = function(files, instanceDir) {
  206. const p = path.join(instanceDir, SHADER_DIR)
  207. exports.validateDir(p)
  208. for(let f of files) {
  209. if(SHADER_REGEX.exec(f.name) != null) {
  210. fs.moveSync(f.path, path.join(p, f.name))
  211. }
  212. }
  213. }