dropinmodutil.js 6.8 KB

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