assetdownload.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. const fs = require('fs')
  2. const request = require('request')
  3. const path = require('path')
  4. const mkpath = require('mkdirp');
  5. const async = require('async')
  6. function Asset(from, to, size){
  7. this.from = from
  8. this.to = to
  9. this.size = size
  10. }
  11. function AssetIndex(id, sha1, size, url, totalSize){
  12. this.id = id
  13. this.sha1 = sha1
  14. this.size = size
  15. this.url = url
  16. this.totalSize = totalSize
  17. }
  18. function ClientDownload(){
  19. }
  20. function ServerDownload(){
  21. }
  22. function Library(){
  23. }
  24. /**
  25. * This function will download the version index data and read it into a Javascript
  26. * Object. This object will then be returned.
  27. */
  28. exports.parseVersionData = function(version, basePath){
  29. const name = version + '.json'
  30. const baseURL = 'https://s3.amazonaws.com/Minecraft.Download/versions/' + version + '/' + name
  31. const versionPath = path.join(basePath, 'versions', version)
  32. return new Promise(function(fulfill, reject){
  33. request.head(baseURL, function(err, res, body){
  34. console.log('Preparing download of ' + version + ' assets.')
  35. mkpath.sync(versionPath)
  36. const stream = request(baseURL).pipe(fs.createWriteStream(path.join(versionPath, name)))
  37. stream.on('finish', function(){
  38. fulfill(JSON.parse(fs.readFileSync(path.join(versionPath, name))))
  39. })
  40. })
  41. })
  42. }
  43. /**
  44. * Download the client for version. This file is 'client.jar' although
  45. * it must be renamed to '{version}'.jar.
  46. */
  47. exports.downloadClient = function(versionData, basePath){
  48. const dls = versionData['downloads']
  49. const clientData = dls['client']
  50. const url = clientData['url']
  51. const size = clientData['size']
  52. const version = versionData['id']
  53. const targetPath = path.join(basePath, 'versions', version)
  54. const targetFile = version + '.jar'
  55. request.head(url, function(err, res, body){
  56. console.log('Downloading ' + version + ' client..')
  57. mkpath.sync(targetPath)
  58. const stream = request(url).pipe(fs.createWriteStream(path.join(targetPath, targetFile)))
  59. stream.on('finish', function(){
  60. console.log('Finished downloading ' + version + ' client.')
  61. })
  62. })
  63. }
  64. exports.downloadLogConfig = function(versionData, basePath){
  65. const logging = versionData['logging']
  66. const client = logging['client']
  67. const file = client['file']
  68. const version = versionData['id']
  69. const targetPath = path.join(basePath, 'assets', 'log_configs')
  70. const name = file['id']
  71. const url = file['url']
  72. request.head(url, function(err, res, body){
  73. console.log('Downloading ' + version + ' log config..')
  74. mkpath.sync(targetPath)
  75. const stream = request(url).pipe(fs.createWriteStream(path.join(targetPath, name)))
  76. stream.on('finish', function(){
  77. console.log('Finished downloading ' + version + ' log config..')
  78. })
  79. })
  80. }
  81. exports.downloadLibraries = function(versionData, basePath){
  82. const libArr = versionData['libraries']
  83. const libPath = path.join(basePath, 'libraries')
  84. async.eachLimit(libArr, 1, function(lib, cb){
  85. if(validateRules(lib['rules'])){
  86. if(lib['natives'] == null){
  87. const dlInfo = lib['downloads']
  88. const artifact = dlInfo['artifact']
  89. const libSize = artifact['size']
  90. const to = path.join(libPath, artifact['path'])
  91. const from = artifact['url']
  92. mkpath.sync(path.join(to, ".."))
  93. let req = request(from)
  94. let writeStream = fs.createWriteStream(to)
  95. req.pipe(writeStream)
  96. let acc = 0;
  97. req.on('data', function(chunk){
  98. acc += chunk.length
  99. console.log('Progress', acc/libSize)
  100. })
  101. writeStream.on('close', function(){
  102. cb()
  103. })
  104. } else {
  105. //TODO Perform native extraction.
  106. }
  107. }
  108. }, function(err){
  109. if(err){
  110. console.log('A file failed to process');
  111. } else {
  112. console.log('All files have been processed successfully');
  113. }
  114. })
  115. }
  116. validateRules = function(rules){
  117. if(rules == null) return true;
  118. rules.forEach(function(rule){
  119. const action = rule['action']
  120. if(action != null){
  121. if(action === 'disallow'){
  122. osName = action['os']
  123. if(osName != null){
  124. if(osName === mojangFriendlyOS()){
  125. return false;
  126. }
  127. }
  128. }
  129. }
  130. })
  131. return true;
  132. }
  133. mojangFriendlyOS = function(){
  134. const opSys = process.platform
  135. if (opSys === 'darwin') {
  136. return 'osx'
  137. } else if (opSys === 'win32'){
  138. return 'windows'
  139. } else if (opSys === 'linux'){
  140. return 'linux'
  141. } else {
  142. return 'unknown_os'
  143. }
  144. }
  145. /**
  146. * Given an index url, this function will asynchonously download the
  147. * assets associated with that version.
  148. */
  149. exports.downloadAssets = function(versionData, basePath){
  150. //Asset index constants.
  151. const assetIndex = versionData['assetIndex']
  152. const indexURL = assetIndex['url']
  153. const datasize = assetIndex['totalSize']
  154. const gameVersion = versionData['id']
  155. const assetVersion = assetIndex['id']
  156. const name = assetVersion + '.json'
  157. //Asset constants
  158. const resourceURL = 'http://resources.download.minecraft.net/'
  159. const localPath = path.join(basePath, 'assets')
  160. const indexPath = path.join(localPath, 'indexes')
  161. const objectPath = path.join(localPath, 'objects')
  162. request.head(indexURL, function (err, res, body) {
  163. console.log('Downloading ' + gameVersion + ' asset index.')
  164. mkpath.sync(indexPath)
  165. const stream = request(indexURL).pipe(fs.createWriteStream(path.join(indexPath, name)))
  166. stream.on('finish', function() {
  167. const data = JSON.parse(fs.readFileSync(path.join(indexPath, name), 'utf-8'))
  168. const assetArr = []
  169. Object.keys(data['objects']).forEach(function(key, index){
  170. const ob = data['objects'][key]
  171. const hash = String(ob['hash'])
  172. const assetName = path.join(hash.substring(0, 2), hash)
  173. const urlName = hash.substring(0, 2) + "/" + hash
  174. const ast = new Asset(resourceURL + urlName, path.join(objectPath, assetName), ob['size'])
  175. assetArr.push(ast)
  176. })
  177. let acc = 0;
  178. async.eachLimit(assetArr, 5, function(asset, cb){
  179. mkpath.sync(path.join(asset.to, ".."))
  180. let req = request(asset.from)
  181. let writeStream = fs.createWriteStream(asset.to)
  182. req.pipe(writeStream)
  183. req.on('data', function(chunk){
  184. acc += chunk.length
  185. console.log('Progress', acc/datasize)
  186. })
  187. writeStream.on('close', function(){
  188. cb()
  189. })
  190. }, function(err){
  191. if(err){
  192. console.log('A file failed to process');
  193. } else {
  194. console.log('All files have been processed successfully');
  195. }
  196. })
  197. })
  198. })
  199. }