dropinmodutil.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  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 {boolean} True if the mod was deleted, otherwise false.
  87. */
  88. exports.deleteDropinMod = function(modsDir, fullName){
  89. const res = shell.moveItemToTrash(path.join(modsDir, fullName))
  90. if(!res){
  91. shell.beep()
  92. }
  93. return res
  94. }
  95. /**
  96. * Toggle a discovered mod on or off. This is achieved by either
  97. * adding or disabling the .disabled extension to the local file.
  98. *
  99. * @param {string} modsDir The path to the mods directory.
  100. * @param {string} fullName The fullName of the discovered mod to toggle.
  101. * @param {boolean} enable Whether to toggle on or off the mod.
  102. *
  103. * @returns {Promise.<void>} A promise which resolves when the mod has
  104. * been toggled. If an IO error occurs the promise will be rejected.
  105. */
  106. exports.toggleDropinMod = function(modsDir, fullName, enable){
  107. return new Promise((resolve, reject) => {
  108. const oldPath = path.join(modsDir, fullName)
  109. const newPath = path.join(modsDir, enable ? fullName.substring(0, fullName.indexOf(DISABLED_EXT)) : fullName + DISABLED_EXT)
  110. fs.rename(oldPath, newPath, (err) => {
  111. if(err){
  112. reject(err)
  113. } else {
  114. resolve()
  115. }
  116. })
  117. })
  118. }
  119. /**
  120. * Check if a drop-in mod is enabled.
  121. *
  122. * @param {string} fullName The fullName of the discovered mod to toggle.
  123. * @returns {boolean} True if the mod is enabled, otherwise false.
  124. */
  125. exports.isDropinModEnabled = function(fullName){
  126. return !fullName.endsWith(DISABLED_EXT)
  127. }
  128. /**
  129. * Scan for shaderpacks inside the shaderpacks folder.
  130. *
  131. * @param {string} instanceDir The path to the server instance directory.
  132. *
  133. * @returns {{fullName: string, name: string}[]}
  134. * An array of objects storing metadata about each discovered shaderpack.
  135. */
  136. exports.scanForShaderpacks = function(instanceDir){
  137. const shaderDir = path.join(instanceDir, SHADER_DIR)
  138. const packsDiscovered = [{
  139. fullName: 'OFF',
  140. name: 'Off (Default)'
  141. }]
  142. if(fs.existsSync(shaderDir)){
  143. let modCandidates = fs.readdirSync(shaderDir)
  144. for(let file of modCandidates){
  145. const match = SHADER_REGEX.exec(file)
  146. if(match != null){
  147. packsDiscovered.push({
  148. fullName: match[0],
  149. name: match[1]
  150. })
  151. }
  152. }
  153. }
  154. return packsDiscovered
  155. }
  156. /**
  157. * Read the optionsshaders.txt file to locate the current
  158. * enabled pack. If the file does not exist, OFF is returned.
  159. *
  160. * @param {string} instanceDir The path to the server instance directory.
  161. *
  162. * @returns {string} The file name of the enabled shaderpack.
  163. */
  164. exports.getEnabledShaderpack = function(instanceDir){
  165. exports.validateDir(instanceDir)
  166. const optionsShaders = path.join(instanceDir, SHADER_CONFIG)
  167. if(fs.existsSync(optionsShaders)){
  168. const buf = fs.readFileSync(optionsShaders, {encoding: 'utf-8'})
  169. const match = SHADER_OPTION.exec(buf)
  170. if(match != null){
  171. return match[1]
  172. } else {
  173. console.warn('WARNING: Shaderpack regex failed.')
  174. }
  175. }
  176. return 'OFF'
  177. }
  178. /**
  179. * Set the enabled shaderpack.
  180. *
  181. * @param {string} instanceDir The path to the server instance directory.
  182. * @param {string} pack the file name of the shaderpack.
  183. */
  184. exports.setEnabledShaderpack = function(instanceDir, pack){
  185. exports.validateDir(instanceDir)
  186. const optionsShaders = path.join(instanceDir, SHADER_CONFIG)
  187. let buf
  188. if(fs.existsSync(optionsShaders)){
  189. buf = fs.readFileSync(optionsShaders, {encoding: 'utf-8'})
  190. buf = buf.replace(SHADER_OPTION, `shaderPack=${pack}`)
  191. } else {
  192. buf = `shaderPack=${pack}`
  193. }
  194. fs.writeFileSync(optionsShaders, buf, {encoding: 'utf-8'})
  195. }
  196. /**
  197. * Add shaderpacks.
  198. *
  199. * @param {FileList} files The files to add.
  200. * @param {string} instanceDir The path to the server instance directory.
  201. */
  202. exports.addShaderpacks = function(files, instanceDir) {
  203. const p = path.join(instanceDir, SHADER_DIR)
  204. exports.validateDir(p)
  205. for(let f of files) {
  206. if(SHADER_REGEX.exec(f.name) != null) {
  207. fs.moveSync(f.path, path.join(p, f.name))
  208. }
  209. }
  210. }