assetdownload.js 8.3 KB

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