dropinmodutil.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. const fs = require('fs-extra')
  2. const path = require('path')
  3. const { shell } = require('electron')
  4. // Group #1: File Name (without .disabled, if any)
  5. // Group #2: File Extension (jar, zip, or litemod)
  6. // Group #3: If it is disabled (if string 'disabled' is present)
  7. const MOD_REGEX = /^(.+(jar|zip|litemod))(?:\.(disabled))?$/
  8. const DISABLED_EXT = '.disabled'
  9. const SHADER_REGEX = /^(.+)\.zip$/
  10. const SHADER_OPTION = /shaderPack=(.+)/
  11. const SHADER_DIR = 'shaderpacks'
  12. const SHADER_CONFIG = 'optionsshaders.txt'
  13. /**
  14. * Validate that the given directory exists. If not, it is
  15. * created.
  16. *
  17. * @param {string} modsDir The path to the mods directory.
  18. */
  19. exports.validateDir = function(dir) {
  20. fs.ensureDirSync(dir)
  21. }
  22. /**
  23. * Scan for drop-in mods in both the mods folder and version
  24. * safe mods folder.
  25. *
  26. * @param {string} modsDir The path to the mods directory.
  27. * @param {string} version The minecraft version of the server configuration.
  28. *
  29. * @returns {{fullName: string, name: string, ext: string, disabled: boolean}[]}
  30. * An array of objects storing metadata about each discovered mod.
  31. */
  32. exports.scanForDropinMods = function(modsDir, version) {
  33. const modsDiscovered = []
  34. if(fs.existsSync(modsDir)){
  35. let modCandidates = fs.readdirSync(modsDir)
  36. let verCandidates = []
  37. const versionDir = path.join(modsDir, version)
  38. if(fs.existsSync(versionDir)){
  39. verCandidates = fs.readdirSync(versionDir)
  40. }
  41. for(let file of modCandidates){
  42. const match = MOD_REGEX.exec(file)
  43. if(match != null){
  44. modsDiscovered.push({
  45. fullName: match[0],
  46. name: match[1],
  47. ext: match[2],
  48. disabled: match[3] != null
  49. })
  50. }
  51. }
  52. for(let file of verCandidates){
  53. const match = MOD_REGEX.exec(file)
  54. if(match != null){
  55. modsDiscovered.push({
  56. fullName: path.join(version, match[0]),
  57. name: match[1],
  58. ext: match[2],
  59. disabled: match[3] != null
  60. })
  61. }
  62. }
  63. }
  64. return modsDiscovered
  65. }
  66. /**
  67. * Add dropin mods.
  68. *
  69. * @param {FileList} files The files to add.
  70. * @param {string} modsDir The path to the mods directory.
  71. */
  72. exports.addDropinMods = function(files, modsdir) {
  73. exports.validateDir(modsdir)
  74. for(let f of files) {
  75. if(MOD_REGEX.exec(f.name) != null) {
  76. fs.moveSync(f.path, path.join(modsdir, f.name))
  77. }
  78. }
  79. }
  80. /**
  81. * Delete a drop-in mod from the file system.
  82. *
  83. * @param {string} modsDir The path to the mods directory.
  84. * @param {string} fullName The fullName of the discovered mod to delete.
  85. *
  86. * @returns {Promise.<boolean>} True if the mod was deleted, otherwise false.
  87. */
  88. exports.deleteDropinMod = async function(modsDir, fullName){
  89. try {
  90. await shell.trashItem(path.join(modsDir, fullName))
  91. return true
  92. } catch(error) {
  93. shell.beep()
  94. console.error('Error deleting drop-in mod.', error)
  95. return false
  96. }
  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. }