Working on experimental features, optimizing downloads.

This commit is contained in:
Daniel Scalzi 2017-05-12 04:30:55 -04:00
parent 8173f85df0
commit f5702f62a6
5 changed files with 204 additions and 110 deletions

View File

@ -22,6 +22,7 @@
* * * *
******************************************************************************/ ******************************************************************************/
/* Reset body, html, and div presets. */
body, html, div { body, html, div {
margin: 0px; margin: 0px;
padding: 0px; padding: 0px;
@ -38,7 +39,7 @@ html {
* * * *
******************************************************************************/ ******************************************************************************/
/* Main div header container/ */ /* Main div header container. */
#header_container { #header_container {
background-color: black; background-color: black;
padding: 5px; padding: 5px;
@ -68,19 +69,53 @@ html {
display: block; display: block;
} }
/* Div container for the social buttons */ /* Div container for the social buttons. */
#header_social_container { #header_social_container {
font-size: 16px;
position: absolute; position: absolute;
bottom: 0px; bottom: 0px;
right: 0px; right: 0px;
margin-right: 5px; margin-bottom: 5px;
} }
/* Social buttons */ /* Social buttons. */
.header_social_img { .header_social_img {
height: 25px; height: 25px;
width: auto; width: auto;
display: inline-block; display: inline-block;
cursor: pointer; cursor: pointer;
margin-right: 5px;
}
/*******************************************************************************
* *
* Left Body Container *
* *
******************************************************************************/
#body_left_container {
width: 25%;
display: inline-block;
}
/*******************************************************************************
* *
* Right Body Container *
* *
******************************************************************************/
#body_right_container {
width: 75%;
display: inline-block;
}
.mtoggle_button {
text-align:centre;
margin:5px 2px;
padding:0.4em 3em;
color:#000;
background-color:#FFF;
border-radius:10px;
display:inline-block;
border:solid 1px #CCC;
cursor:pointer;
} }

View File

@ -4,7 +4,8 @@ const path = require('path')
const mkpath = require('mkdirp'); const mkpath = require('mkdirp');
const async = require('async') const async = require('async')
const crypto = require('crypto') const crypto = require('crypto')
const libary = require('./library.js') const library = require('./library.js')
const {BrowserWindow} = require('electron')
function Asset(from, to, size, hash){ function Asset(from, to, size, hash){
this.from = from this.from = from
@ -21,11 +22,19 @@ function AssetIndex(id, sha1, size, url, totalSize){
this.totalSize = totalSize this.totalSize = totalSize
} }
function Library(id, sha1, size, from, to){
this.id = id
this.sha1 = sha1
this.size = size
this.from = from
this.to = to
}
/** /**
* This function will download the version index data and read it into a Javascript * This function will download the version index data and read it into a Javascript
* Object. This object will then be returned. * Object. This object will then be returned.
*/ */
exports.parseVersionData = function(version, basePath){ parseVersionData = function(version, basePath){
const name = version + '.json' const name = version + '.json'
const baseURL = 'https://s3.amazonaws.com/Minecraft.Download/versions/' + version + '/' + name const baseURL = 'https://s3.amazonaws.com/Minecraft.Download/versions/' + version + '/' + name
const versionPath = path.join(basePath, 'versions', version) const versionPath = path.join(basePath, 'versions', version)
@ -46,7 +55,7 @@ exports.parseVersionData = function(version, basePath){
* Download the client for version. This file is 'client.jar' although * Download the client for version. This file is 'client.jar' although
* it must be renamed to '{version}'.jar. * it must be renamed to '{version}'.jar.
*/ */
exports.downloadClient = function(versionData, basePath){ downloadClient = function(versionData, basePath){
const dls = versionData['downloads'] const dls = versionData['downloads']
const clientData = dls['client'] const clientData = dls['client']
const url = clientData['url'] const url = clientData['url']
@ -68,7 +77,7 @@ exports.downloadClient = function(versionData, basePath){
} }
} }
exports.downloadLogConfig = function(versionData, basePath){ downloadLogConfig = function(versionData, basePath){
const logging = versionData['logging'] const logging = versionData['logging']
const client = logging['client'] const client = logging['client']
const file = client['file'] const file = client['file']
@ -90,73 +99,53 @@ exports.downloadLogConfig = function(versionData, basePath){
} }
} }
exports.downloadLibraries = function(versionData, basePath){ downloadLibraries = function(versionData, basePath){
const libArr = versionData['libraries'] const libArr = versionData['libraries']
const libPath = path.join(basePath, 'libraries') const libPath = path.join(basePath, 'libraries')
async.eachLimit(libArr, 1, function(lib, cb){
if(library.validateRules(lib['rules'])){ let win = BrowserWindow.getFocusedWindow()
if(lib['natives'] == null){ const libDlQueue = []
const dlInfo = lib['downloads'] let dlSize = 0
const artifact = dlInfo['artifact']
const sha1 = artifact['sha1'] //Check validity of each library. If the hashs don't match, download the library.
const libSize = artifact['size'] libArr.forEach(function(lib, index){
const to = path.join(libPath, artifact['path']) if(library.validateRules(lib.rules)){
const from = artifact['url'] let artifact = null
if(!validateLocalIntegrity(to, 'sha1', sha1)){ if(lib.natives == null){
mkpath.sync(path.join(to, "..")) artifact = lib.downloads.artifact
let req = request(from)
let writeStream = fs.createWriteStream(to)
req.pipe(writeStream)
let acc = 0;
req.on('data', function(chunk){
acc += chunk.length
//console.log('Progress', acc/libSize)
})
writeStream.on('close', function(){
cb()
})
} else {
cb()
}
} else { } else {
const natives = lib['natives'] artifact = lib.downloads.classifiers[lib.natives[library.mojangFriendlyOS()]]
const opSys = library.mojangFriendlyOS() }
const indexId = natives[opSys] const libItm = new Library(lib.name, artifact.sha1, artifact.size, artifact.url, path.join(libPath, artifact.path))
const dlInfo = lib['downloads'] if(!validateLocalIntegrity(libItm.to, 'sha1', libItm.sha1)){
const classifiers = dlInfo['classifiers'] dlSize += libItm.size
const artifact = classifiers[indexId] libDlQueue.push(libItm)
const libSize = artifact['size']
const to = path.join(libPath, artifact['path'])
const from = artifact['url']
const sha1 = artifact['sha1']
if(!validateLocalIntegrity(to, 'sha1', sha1)){
mkpath.sync(path.join(to, ".."))
let req = request(from)
let writeStream = fs.createWriteStream(to)
req.pipe(writeStream)
let acc = 0;
req.on('data', function(chunk){
acc += chunk.length
console.log('Progress', acc/libSize)
})
writeStream.on('close', function(){
cb()
})
} else {
cb()
}
} }
} else {
cb()
} }
})
let acc = 0;
//Download all libraries that failed validation.
async.eachLimit(libDlQueue, 1, function(lib, cb){
mkpath.sync(path.join(lib.to, '..'))
let req = request(lib.from)
let writeStream = fs.createWriteStream(lib.to)
req.pipe(writeStream)
req.on('data', function(chunk){
acc += chunk.length
//console.log('Progress', acc/dlSize)
win.setProgressBar(acc/dlSize)
})
writeStream.on('close', cb)
}, function(err){ }, function(err){
if(err){ if(err){
console.log('A library failed to process'); console.log('A library failed to process');
} else { } else {
console.log('All libraries have been processed successfully'); console.log('All libraries have been processed successfully');
} }
win.setProgressBar(-1)
}) })
} }
@ -164,13 +153,12 @@ exports.downloadLibraries = function(versionData, basePath){
* Given an index url, this function will asynchonously download the * Given an index url, this function will asynchonously download the
* assets associated with that version. * assets associated with that version.
*/ */
exports.downloadAssets = function(versionData, basePath){ downloadAssets = function(versionData, basePath){
//Asset index constants. //Asset index constants.
const assetIndex = versionData['assetIndex'] const assetIndex = versionData.assetIndex
const indexURL = assetIndex['url'] const indexURL = assetIndex.url
const datasize = assetIndex['totalSize'] const gameVersion = versionData.id
const gameVersion = versionData['id'] const assetVersion = assetIndex.id
const assetVersion = assetIndex['id']
const name = assetVersion + '.json' const name = assetVersion + '.json'
//Asset constants //Asset constants
@ -178,46 +166,51 @@ exports.downloadAssets = function(versionData, basePath){
const localPath = path.join(basePath, 'assets') const localPath = path.join(basePath, 'assets')
const indexPath = path.join(localPath, 'indexes') const indexPath = path.join(localPath, 'indexes')
const objectPath = path.join(localPath, 'objects') const objectPath = path.join(localPath, 'objects')
let win = BrowserWindow.getFocusedWindow()
request.head(indexURL, function (err, res, body) { const assetIndexLoc = path.join(indexPath, name)
console.log('Downloading ' + gameVersion + ' asset index.') /*if(!fs.existsSync(assetIndexLoc)){
mkpath.sync(indexPath)
const stream = request(indexURL).pipe(fs.createWriteStream(path.join(indexPath, name))) }*/
stream.on('finish', function() { console.log('Downloading ' + gameVersion + ' asset index.')
const data = JSON.parse(fs.readFileSync(path.join(indexPath, name), 'utf-8')) mkpath.sync(indexPath)
const assetArr = [] const stream = request(indexURL).pipe(fs.createWriteStream(assetIndexLoc))
Object.keys(data['objects']).forEach(function(key, index){ stream.on('finish', function() {
const ob = data['objects'][key] const data = JSON.parse(fs.readFileSync(assetIndexLoc, 'utf-8'))
const hash = String(ob['hash']) const assetDlQueue = []
const assetName = path.join(hash.substring(0, 2), hash) let dlSize = 0;
const urlName = hash.substring(0, 2) + "/" + hash Object.keys(data.objects).forEach(function(key, index){
const ast = new Asset(resourceURL + urlName, path.join(objectPath, assetName), ob['size'], hash) const ob = data.objects[key]
assetArr.push(ast) const hash = ob.hash
}) const assetName = path.join(hash.substring(0, 2), hash)
let acc = 0; const urlName = hash.substring(0, 2) + "/" + hash
async.eachLimit(assetArr, 5, function(asset, cb){ const ast = new Asset(resourceURL + urlName, path.join(objectPath, assetName), ob.size, String(ob.hash))
mkpath.sync(path.join(asset.to, "..")) if(!validateLocalIntegrity(ast.to, 'sha1', ast.hash)){
if(!validateLocalIntegrity(asset.to, 'sha1', asset.hash)){ dlSize += ast.size
let req = request(asset.from) assetDlQueue.push(ast)
let writeStream = fs.createWriteStream(asset.to) }
req.pipe(writeStream) })
req.on('data', function(chunk){
acc += chunk.length let acc = 0;
//console.log('Progress', acc/datasize) async.eachLimit(assetDlQueue, 5, function(asset, cb){
}) mkpath.sync(path.join(asset.to, ".."))
writeStream.on('close', function(){ let req = request(asset.from)
cb() let writeStream = fs.createWriteStream(asset.to)
}) req.pipe(writeStream)
} else { req.on('data', function(chunk){
cb() acc += chunk.length
} console.log('Progress', acc/dlSize)
}, function(err){ win.setProgressBar(acc/dlSize)
if(err){
console.log('An asset failed to process');
} else {
console.log('All assets have been processed successfully');
}
}) })
writeStream.on('close', cb)
}, function(err){
if(err){
console.log('An asset failed to process');
} else {
console.log('All assets have been processed successfully');
}
win.setProgressBar(-1)
}) })
}) })
} }
@ -239,4 +232,12 @@ validateLocalIntegrity = function(filePath, algo, hash){
} }
} }
return false; return false;
}
module.exports = {
parseVersionData,
downloadClient,
downloadLogConfig,
downloadLibraries,
downloadAssets
} }

View File

@ -0,0 +1,44 @@
const app = require('electron')
const remote = require('electron').BrowserWindow
/**
* Doesn't work yet.
*/
exports.setIconBadge = function(text){
if(process.platform === 'darwin'){
app.dock.setBadge('' + text)
} else if (process.platform === 'win32'){
const win = remote.getFocusedWindow()
if(text === ''){
win.setOverlayIcon(null, '')
return;
}
//Create badge
const canvas = document.createElement('canvas')
canvas.height = 140;
canvas.width = 140;
const ctx = canvas.getContext('2d')
ctx.fillStyle = '#a02d2a'
ctx.beginPath()
ctx.ellipse(70, 70, 70, 70, 0, 0, 2 * Math.PI)
ctx.fill()
ctx.textAlign = 'center'
ctx.fillStyle = 'white'
if(text.length > 2 ){
ctx.font = '75px sans-serif'
ctx.fillText('' + text, 70, 98)
} else if (text.length > 1){
ctx.font = '100px sans-serif'
ctx.fillText('' + text, 70, 105)
} else {
ctx.font = '125px sans-serif'
ctx.fillText('' + text, 70, 112)
}
const badgeDataURL = canvas.toDataURL()
const img = NativeImage.createFromDataURL(badgeDataURL)
win.setOverlayIcon(img, '' + text)
}
}

View File

@ -27,5 +27,18 @@
</a> </a>
</div> </div>
</div> </div>
<div id="body_left_container">
</div>
<div id="body_right_container">
<div id="main_toggle_group">
<div><label><input type="radio" name="main_group" class="mtoggle_button"/>News</label></div>
<div><label><input type="radio" name="main_group" class="mtoggle_button"/>Map</label></div>
<div><label><input type="radio" name="main_group" class="mtoggle_button"/>Mods</label></div>
<div><label><input type="radio" name="main_group" class="mtoggle_button"/>FAQ</label></div>
<div><label><input type="radio" name="main_group" class="mtoggle_button"/>Log</label></div>
<div><label><input type="radio" name="main_group" class="mtoggle_button"/>Settings</label></div>
</div>
</div>
</body> </body>
</html> </html>

View File

@ -22,11 +22,12 @@ function createWindow() {
const basePath = path.join(__dirname, 'mcfiles') const basePath = path.join(__dirname, 'mcfiles')
const dataPromise = assetdl.parseVersionData('1.11.2', basePath) const dataPromise = assetdl.parseVersionData('1.11.2', basePath)
dataPromise.then(function(data){ dataPromise.then(function(data){
assetdl.downloadAssets(data, basePath)
//assetdl.downloadAssets(data, basePath) //assetdl.downloadAssets(data, basePath)
//assetdl.downloadClient(data, basePath) //assetdl.downloadClient(data, basePath)
//assetdl.downloadLogConfig(data, basePath) //assetdl.downloadLogConfig(data, basePath)
//assetdl.downloadLibraries(data, basePath) //assetdl.downloadLibraries(data, basePath)
require('./app/assets/js/launchprocess.js').launchMinecraft(data, basePath) //require('./app/assets/js/launchprocess.js').launchMinecraft(data, basePath)
})*/ })*/
win.on('closed', () => { win.on('closed', () => {